How to create graphs in Rails? - ruby-on-rails

Does anyone know how do I create graphs in Rails. This is because I need to read and present statistics in rails and the best way to present the statistics is in a graph. So can anyone tell me how do I do so? Thanks! :)

There's a lot of options here. To try and keep things within Ruby I'd recommend checking out the ruport gem. Note that the last release of Ruport was in 2007.
As Calvin mentioned the other options involve Javascript solutions. There are two libraries that I like. The first is D3.js and can be found here:
http://d3js.org/
D3 is pretty flexible and has some powerful data manipulation tools for working with your data, however it tends to take a bit more development time to get something nice.
The other option I'd recommend is highcharts:
http://www.highcharts.com/
This library isn't as flexible as D3, but its much easier to get a really pretty chart up and running fast. The other thing is that it doesn't really have the data manipulation tools that D3 has. If you're looking for something simple though, I'd recommend trying Highcharts first.

I have been using a JavaScript library called jqPLot. It's pretty easy to pick up - basically, you initialize the chart through JS. But, in that JS you can use rails script tags <% %> to build the necessary arrays of data to feed the cart. There are obviously other ways to load the data as well.
As a quick example, this would be you html page (call it graph.html.erb). The controller that renders this page needs to set the #user_stats variable, so it can be used to build the data set for jqPlot.
<html>
<head>
<script type="text/javascript">
$(document).ready(function() {
var plot2 = $.jqplot('chartdiv',
<%
last_one = #user_stats.pop
%>
[[
<% #user_stats.each { |user_stat| %>
[<%= user_stat[1] %>, '<%= user_stat[0].first_name %>'],
<% } %>
[<%= last_one[1] %>, '<%= last_one[0].first_name %>']
]],
{
seriesDefaults: {
renderer:$.jqplot.BarRenderer,
// Show point labels to the right ('e'ast) of each bar.
// edgeTolerance of -15 allows labels flow outside the grid
// up to 15 pixels. If they flow out more than that, they
// will be hidden.
pointLabels: { show: true, location: 'e', edgeTolerance: -15 },
// Rotate the bar shadow as if bar is lit from top right.
shadowAngle: 135,
// Here's where we tell the chart it is oriented horizontally.
rendererOptions: {
barDirection: 'horizontal'
}
},
axes: {
yaxis: {
renderer: $.jqplot.CategoryAxisRenderer
}
}
});
</script>
</head>
<body>
<div id="chartdiv" style="height:400px;width:300px;"></div>
</body>
</html>

I happened along your question while looking to do some charting in my own rails app.
I would suggest taking a look at Chartkick, here: http://ankane.github.io/chartkick/
It's relatively new, so was not available when you asked your original question. It wraps the Google charts API in a simple Rails gem (you can also point it to use the Highcharts core if you wish). I was able to get some basic charts together in a couple of hours... a huge plus is that I didn't have to write any JavaScript to get it working. I did have to write a bit of SQL to get the data labels to come out correctly, since the data that I'm pulling is spread across multiple tables.
Good luck!

The best way to accomplish this is to use Google Chart Tool. They have a lot of common graph types ready to be used, so I'd take a look at that first.
Here's a quick example of what your code may look like
// Load the Visualization API and the piechart package.
google.load('visualization', '1.0', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawChartRepublican);
function drawChartRepublican() {
// Create the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Topping');
data.addColumn('number', 'Slices');
data.addRows([
['Newt Gingrich', <%= #gingrich_today %>],
['Mitt Romney', <%= #romney_today %>],
['Ron Paul', <%= #paul_today %>],
['Rick Perry', <%= #perry_today %>],
['Michelle Bachmann', <%= #bachmann_today %>],
['Rick Santorum', <%= #santorum_today %>],
['John Huntsman', <%= #huntsman_today %>],
['Buddy Roemer', <%= #roemer_today %>]
]);
// Set chart options
var options = {'title':'Scoreboard (<%= DateTime.now %>)', 'width':680, 'height':480};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.PieChart(document.getElementById('chart_div_republican'));
chart.draw(data, options);
}
In the view, you add the graph this way.
<div id="chart_div_republican"></div>

You should probably use Javascript for your charts. You can technically create them with Rails and some HTML/CSS, but your views will get a bit convoluted.
I think the best way is to use Rails to render JSON data, then import that JSON data into a JS library like Flot to render the actual graphs.

Related

RainLoop emails autocomplete and additional data attributes

I'm trying to filter output for emials autocomplete suggestions as I need to add data attributes to them using ajax.suggestions-post hook but I'm not able to locate code responsible for rendering an output for
$this->Plugins()->RunHook('ajax.suggestions-post', array(&$aResult, $sQuery, $oAccount, $iLimit));
if ($iLimit < \count($aResult))
{
$aResult = \array_slice($aResult, 0, $iLimit);
}
return $this->DefaultResponse(__FUNCTION__, $aResult);
It is a part of DoSuggestions() function which uses autocomplete from jQuery UI but I'm missing part where is functionality to get $aResult bits and split them between "Full Name" bit and <email> bit for a suggestions which output looks like:
<div id="ui-id-X" tabindex="-1" class="ui-menu-item-wrapper">"Full Name" <myemail#test.com></div>
Any tips how $aResult array can be passed to jQuery UI autocomplete?
It came up is not done via jQuery but RainLoop's JavaScript oEmailItem.toLine function (app.js file).

Way to organize client-side templates into individual files?

I'm using Handlebars.js, and currently all my templates live inside script tags which live inside .html files housing dozens of other templates, also inside script tags.
<script type="text/template" id="template-1">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-2">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-3">
<div>{{variable}}</div>
</script>
...
Then I include this file on the server-side as a partial.
This has the following disadvantages:
A bunch of templates are crammed into HTML files.
Finding a given template is tedious.
I'm looking for a better way to organize my templates. I'd like each each template to live in its own file. For example:
/public/views/my_controller/my_action/some_template.html
/public/views/my_controller/my_action/some_other_template.html
/public/views/my_controller/my_other_action/another_template.html
/public/views/my_controller/my_other_action/yet_another_template.html
/public/views/shared/my_shared_template.html
Then at the top of my view, in the backend code, I can include these templates when the page loads, like this:
SomeTemplateLibrary.require(
"/public/views/my_controller/my_action/*",
"/public/views/shared/my_shared_template.html"
)
This would include all templates in /public/views/my_controller/my_action/ and also include /public/views/shared/my_shared_template.html.
My question: Are there any libraries out there that provide this or similar functionality? Or, does anyone have any alternative organizational suggestions?
RequireJS is really good library for AMD style dependency management. You can actually use the 'text' plugin of requireJS to load the template file in to your UI component. Once the template is attached to the DOM, you may use any MVVM, MVC library for bindings OR just use jQuery events for your logic.
I'm the author of BoilerplateJS. BoilerplateJS reference architecture uses requireJS for dependency management. It also provides a reference implementations to show how a self contained UI Components should be created. Self contained in the sense to handle its own view template, code behind, css, localization files, etc.
There is some more information available on the boilerplateJS homepage, under "UI components".
http://boilerplatejs.org/
I ended up using RequireJS, which pretty much let me do this. See http://aaronhardy.com/javascript/javascript-architecture-requirejs-dependency-management/.
I use a template loader that loads the template using ajax the first time it is needed, and caches it locally for future requests. I also use a debug variable to make sure the template is not cached when I am in development:
var template_loader = {
templates_cache : {},
load_template : function load_template (params, callback) {
var template;
if (this.templates_cache[params.url]){
callback(this.templates_cache[params.url]);
}
else{
if (debug){
params.url = params.url + '?t=' + new Date().getTime(), //add timestamp for dev (avoid caching)
console.log('avoid caching url in template loader...');
}
$.ajax({
url: params.url,
success: function(data) {
template = Handlebars.compile(data);
if (params.cache){
this.templates_cache[params.url] = template;
}
callback(template);
}
});
}
}
};
The template is loaded like this:
template_loader.load_template({url: '/templates/mytemplate.handlebars'}, function (template){
var template_data = {}; //get your data
$('#holder').html(template(template_data)); //render
})
there's this handy little jquery plugin I wrote for exactly this purpose.
https://github.com/cultofmetatron/handlebar-helper

Graph background-image resizing without PHP

I've read several helpful answers in re. image resizing using PHP and max-height etc.: Image resize script
However, my problem is that I want to resize an image of a graph that I am retrieving from another site (USGS), and putting into a site (zenfolio) that supports HTML and JavaScript, but not PHP. I have tried adjusting the specified height and width, but keep on ending up resizing only the amount of the image that shows on the page, and not the image itself (sorry I cannot post images as I am a new user).
I just posted them as png's above to demonstrate the problem, but the images are generated as follows:
<div id="riverlevels">
<center>
<div id="MyWidget" style="background-image:url(http://waterdata.usgs.gov/nwisweb/graph?agency_cd=USGS&site_no=12354500&parm_cd=00065&period=21);width:576px;Height:400px;">
<br/>
Montana River Photography </div>
</center>
</div>
</div>
This same image can be generated using this JavaScript, but for some reason that does not allow me to display more than one variable graph per page (I want to show both discharge (00060), and gage height (00065)):
<script type="text/javascript">
wStation = "12354500";
wDays = "21";
wType = "00065";
wWidth = "576px";
wHeight = "400px";
wFColor = "#000033";
wTitle = "";
document.write('<div id="gageheight"></div>');
document.write('<scr'+'ipt type="text/JavaScript"src="http://batpigandme.com/js/showstring.js"></scr'+'ipt>');
As you can tell, I have to use a separate site that I own to create the JavaScript file. The graphs are currently located in various iterations at:
montanariverphoto.com/test
clark fork gage height
I sincerely apologize if I have missed an obvious answer to this! I basically created this widget by reverse engineering a widget from another site, so perhaps my call is incorrect all together.
Does it absolutely have to be a background image? Scaling them is possible (using background-size), but this property is not well supported (basically it won't work in Internet Explorer). Your code would work almost as-is if you can use an image tag instead:
<img src="http://waterdata.usgs.gov/nwisweb/graph?agency_cd=USGS&site_no=12354500&parm_cd=00065&period=21" width="576" height="400" alt="..." />
for your other problem, ids need to be unique on a page. In your code example you are creating a div with the id of gageheight, and this is ID is hardcoded into your javascript file at http://batpigandme.com/js/showstring.js. Since you can only have one element with this ID on the page, if you repeat the code later on it won't work. You'd need to change this script so that you could pass in the ID as a variable, something like:
wTitle = "";
wElement = "gageheight";
document.write('<div id="gageheight"></div>');
document.write('<scr'+'ipt type="text/JavaScript"src="http://batpigandme.com/js/showstring.js"></scr'+'ipt>');
and then in your JS:
var myElement = document.getElementById(wElement);
var JavaScriptCode = document.createElement("script");
JavaScriptCode.setAttribute('type', 'text/javascript');
JavaScriptCode.setAttribute("src", 'http://batpigandme.com/js/data2.js');
myElement.appendChild(JavaScriptCode);

Dijit Tooltip that works on all html tags

So I'm trying to use dijit tooltip.
http://docs.dojocampus.org/dijit/Tooltip
However, they only have the attribute of "connectIds" this seems rather limiting and I'm surprised that it was programmed this way. I don't know how many hyperlinks my pages will have, so wouldn't it be better to have an option like "connectByHTMLtag" so that I can map all "a" tags to a specific tooltip? Or even a "connectClasses" would make a bit more sense.
This means I have to manually enter id="tooltip1" id="tooltip2" etc.
Anyone find a way around this??
You could connect them when the page loads using dojo.query.
Give all of your hyperlinks a class that you can use to select them later:
Etc
Then in your JavaScript you can use something like this:
dojo.addOnLoad(function() {
dojo.query(".link-tooltip").forEach(function(node, index, arr) {
new dijit.Tooltip({
connectId: [node.id],
label: "My tooltip!"
});
});
});
This code is untested, but that's basically how you could do it. dojo.query is very handy for this sort of thing!
As of Dojo Toolkit 1.8, it is now possible to attach a tooltip via a selector:
require(["dojo/ready", "dijit/Tooltip", "dojo/query!css2"], function(ready, Tooltip){
ready(function(){
new Tooltip({
connectId: "myTable",
selector: "tr",
getContent: function(matchedNode){
return matchedNode.getAttribute("tooltipText");
}
});
});
});
http://dojotoolkit.org/reference-guide/1.8/dijit/Tooltip.html#attaching-to-multiple-nodes

(Rails) Need some javascript assistance

I currently have an application that calls creates and displays charts from various objects' data using JS. However, I'm having some severe issues. Can someone please explain why the following code works just fine when statically inserted into a page, but when used via rjs "page.replace_html my_div_id" it removes EVERYTHING ELSE on the page:
<script language="JavaScript" type="text/javascript">
<!--
if (AC_FL_RunContent == 0 || DetectFlashVer == 0) {
alert("This page requires AC_RunActiveContent.js.");
} else {
var hasRightVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
if(hasRightVersion) {
AC_FL_RunContent(
'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,45,0',
'width', '800',
'height', '500',
'scale', 'noscale',
'salign', 'TL',
'bgcolor', '#777788',
'wmode', 'opaque',
'movie', 'charts',
'src', 'charts',
'FlashVars', 'library_path=xmlswfitems/charts_library&xml_source=xmlcharts/M1 Building One',
'id', 'my_chart',
'name', 'M1 Building One',
'menu', 'true',
'allowFullScreen', 'true',
'allowScriptAccess','sameDomain',
'quality', 'high',
'align', 'middle',
'pluginspage', 'http://www.macromedia.com/go/getflashplayer',
'play', 'true',
'devicefont', 'false'
);
} else {
var alternateContent = 'This content requires the Adobe Flash Player. '
+ '<u><a href=http://www.macromedia.com/go/getflash/>Get Flash</a></u>.';
document.write(alternateContent);
}
}
// -->
</script>
...also, it completely fails with IE. My only leads are from Safari ("unmatched embed tag"), Firefox (browser pretends chart never loads even though it has), IE (non-specific prototype.js error). FYI, I'm using XML/SWF Charts. I'm writing this code from scratch as I have needs not met by the existing/outdated SWFCharts library so please don't suggest solutions involving that particular library.
Best.
Use a standard library like SWFObject to embed your flash. It takes care of all the crossbrowser quirks for you and let's you do both static and dynamic publishing with regular html to fall back on if the user does not have flash.
AC_FL_RunContent uses document.write to generate the <object>/<embed> tags, which, if called after the page is completely loaded, replaces the entire contents of the page.
You will probably need to use SWFObject; as far as I know, it doesn't use document.write so it should work anytime.

Resources