grapht 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +20 -0
 - data/.rspec +3 -0
 - data/Gemfile +6 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +267 -0
 - data/Rakefile +1 -0
 - data/bin/grapht +17 -0
 - data/example-data/bar_data.json +5 -0
 - data/example-data/donut_data.json +5 -0
 - data/example-data/grapht_performance_line_data.json +7 -0
 - data/example-data/line_data.json +16 -0
 - data/grapht.gemspec +24 -0
 - data/lib/graph-definitions/bar-horizontal.js +90 -0
 - data/lib/graph-definitions/bar-vertical.js +89 -0
 - data/lib/graph-definitions/donut.js +47 -0
 - data/lib/graph-definitions/line.js +111 -0
 - data/lib/grapht.rb +6 -0
 - data/lib/grapht/shell.rb +18 -0
 - data/lib/grapht/type.rb +5 -0
 - data/lib/grapht/version.rb +3 -0
 - data/script/grapht.coffee +107 -0
 - data/spec/lib/grapht/shell_spec.rb +79 -0
 - data/spec/spec_helper.rb +84 -0
 - data/spec/support/rspec-prof.rb +8 -0
 - data/vendor/d3.min.js +5 -0
 - data/vendor/json2.js +489 -0
 - metadata +121 -0
 
| 
         @@ -0,0 +1,89 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            function(data) {
         
     | 
| 
      
 2 
     | 
    
         
            +
              var margin     = { top: 50, right: 20, bottom: 30, left: 40 },
         
     | 
| 
      
 3 
     | 
    
         
            +
                  width      = 400,
         
     | 
| 
      
 4 
     | 
    
         
            +
                  height     = 400,
         
     | 
| 
      
 5 
     | 
    
         
            +
                  svgWidth   = width + margin.right + margin.left,
         
     | 
| 
      
 6 
     | 
    
         
            +
                  svgHeight  = height + margin.top + margin.bottom;
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              var svg = d3.select("body").append("svg")
         
     | 
| 
      
 9 
     | 
    
         
            +
                         .style("fill", "none")
         
     | 
| 
      
 10 
     | 
    
         
            +
                         .attr("width", svgWidth)
         
     | 
| 
      
 11 
     | 
    
         
            +
                         .attr("height", svgHeight)
         
     | 
| 
      
 12 
     | 
    
         
            +
                         .attr('viewBox', "0 -10 " +svgWidth+ " " +svgHeight+ "")
         
     | 
| 
      
 13 
     | 
    
         
            +
                        .append('g')
         
     | 
| 
      
 14 
     | 
    
         
            +
                          .attr('transform', 'translate(' +margin.left+ ',' +margin.top+ ')')
         
     | 
| 
      
 15 
     | 
    
         
            +
                          .style("fill", "none");
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              // Axis Scales
         
     | 
| 
      
 18 
     | 
    
         
            +
              var x = d3.scale
         
     | 
| 
      
 19 
     | 
    
         
            +
                      .ordinal()
         
     | 
| 
      
 20 
     | 
    
         
            +
                      .domain(data.map(function(d) { return d.name; }))
         
     | 
| 
      
 21 
     | 
    
         
            +
                      .rangeRoundBands([0, width], 0.2);
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              var y = d3.scale
         
     | 
| 
      
 24 
     | 
    
         
            +
                      .linear()
         
     | 
| 
      
 25 
     | 
    
         
            +
                      .domain([0, d3.max(data, function(d) { return d.value; })])
         
     | 
| 
      
 26 
     | 
    
         
            +
                      .range([height, 0])
         
     | 
| 
      
 27 
     | 
    
         
            +
                      .nice();
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              // Axes
         
     | 
| 
      
 30 
     | 
    
         
            +
              var xAxis = d3.svg
         
     | 
| 
      
 31 
     | 
    
         
            +
                          .axis()
         
     | 
| 
      
 32 
     | 
    
         
            +
                          .scale(x)
         
     | 
| 
      
 33 
     | 
    
         
            +
                          .orient('bottom')
         
     | 
| 
      
 34 
     | 
    
         
            +
                          .ticks(data.length);
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              var yAxis = d3.svg
         
     | 
| 
      
 37 
     | 
    
         
            +
                          .axis()
         
     | 
| 
      
 38 
     | 
    
         
            +
                          .scale(y)
         
     | 
| 
      
 39 
     | 
    
         
            +
                          .orient('left')
         
     | 
| 
      
 40 
     | 
    
         
            +
                          .tickSize(width);
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              svg.selectAll('.bar')
         
     | 
| 
      
 43 
     | 
    
         
            +
                  .data(data)
         
     | 
| 
      
 44 
     | 
    
         
            +
                .enter().append('rect')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  .attr('class',    'bar')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  .style('fill',    '#428bca')
         
     | 
| 
      
 47 
     | 
    
         
            +
                  .attr('opacity',  '0.3')
         
     | 
| 
      
 48 
     | 
    
         
            +
                  .attr('y',        function(d) { return y(d.value); })
         
     | 
| 
      
 49 
     | 
    
         
            +
                  .attr('x',        function(d) { return x(d.name); })
         
     | 
| 
      
 50 
     | 
    
         
            +
                  .attr('height',   function(d) { return height - y(d.value); })
         
     | 
| 
      
 51 
     | 
    
         
            +
                  .attr('width',    x.rangeBand());
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              svg.selectAll('.text')
         
     | 
| 
      
 54 
     | 
    
         
            +
                .attr('font-family', 'Arial')
         
     | 
| 
      
 55 
     | 
    
         
            +
                .selectAll('label')
         
     | 
| 
      
 56 
     | 
    
         
            +
                  .attr('fill-opacity', '0.3')
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              var xAxisElement = svg.append('g')
         
     | 
| 
      
 59 
     | 
    
         
            +
                                 .attr('class',      'x axis')
         
     | 
| 
      
 60 
     | 
    
         
            +
                                 .attr('transform',  "translate(0," +height+ ")")
         
     | 
| 
      
 61 
     | 
    
         
            +
                                 .call(xAxis);
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              var yAxisElement = svg.append('g')
         
     | 
| 
      
 64 
     | 
    
         
            +
                                 .attr('class', 'y axis')
         
     | 
| 
      
 65 
     | 
    
         
            +
                                 .attr("transform", "translate(" +width+ ",0)")
         
     | 
| 
      
 66 
     | 
    
         
            +
                                 .call(yAxis);
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              xAxisElement.selectAll('line')
         
     | 
| 
      
 69 
     | 
    
         
            +
                .style('fill', 'none')
         
     | 
| 
      
 70 
     | 
    
         
            +
                .attr('stroke', 'black')
         
     | 
| 
      
 71 
     | 
    
         
            +
                .attr('stroke-width', '1px')
         
     | 
| 
      
 72 
     | 
    
         
            +
                .attr('stroke-opacity', 0.1)
         
     | 
| 
      
 73 
     | 
    
         
            +
                .attr('shape-rendering', 'geometricPrecision');
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              xAxisElement.selectAll('text')
         
     | 
| 
      
 76 
     | 
    
         
            +
                .style('fill', '#333333')
         
     | 
| 
      
 77 
     | 
    
         
            +
                .attr('font-family', 'Arial');
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              yAxisElement.selectAll('line')
         
     | 
| 
      
 80 
     | 
    
         
            +
                .style('fill', 'none')
         
     | 
| 
      
 81 
     | 
    
         
            +
                .attr('stroke', 'black')
         
     | 
| 
      
 82 
     | 
    
         
            +
                .attr('stroke-width', '1px')
         
     | 
| 
      
 83 
     | 
    
         
            +
                .attr('stroke-opacity', 0.1)
         
     | 
| 
      
 84 
     | 
    
         
            +
                .attr('shape-rendering', 'geometricPrecision');
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              yAxisElement.selectAll('text')
         
     | 
| 
      
 87 
     | 
    
         
            +
                .style('fill', '#333333')
         
     | 
| 
      
 88 
     | 
    
         
            +
                .attr('font-family', 'Arial');
         
     | 
| 
      
 89 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            function(data) {
         
     | 
| 
      
 2 
     | 
    
         
            +
              var margin     = { top: 20, right: 20, bottom: 10, left: 10 },
         
     | 
| 
      
 3 
     | 
    
         
            +
                  width      = 400,
         
     | 
| 
      
 4 
     | 
    
         
            +
                  height     = 400,
         
     | 
| 
      
 5 
     | 
    
         
            +
                  svgWidth   = width + margin.right + margin.left,
         
     | 
| 
      
 6 
     | 
    
         
            +
                  svgHeight  = height + margin.top + margin.bottom,
         
     | 
| 
      
 7 
     | 
    
         
            +
                  radius     = Math.min(width, height) / 2;
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              var svg = d3.select("body").append("svg")
         
     | 
| 
      
 10 
     | 
    
         
            +
                         .style("fill", "none")
         
     | 
| 
      
 11 
     | 
    
         
            +
                         .attr("width", svgWidth)
         
     | 
| 
      
 12 
     | 
    
         
            +
                         .attr("height", svgHeight)
         
     | 
| 
      
 13 
     | 
    
         
            +
                         .attr('viewBox', "0 -10 " +svgWidth+ " " +svgHeight+ "")
         
     | 
| 
      
 14 
     | 
    
         
            +
                        .append('g')
         
     | 
| 
      
 15 
     | 
    
         
            +
                          .attr('transform', 'translate(' +(width/2)+ ',' +(height/2)+ ')')
         
     | 
| 
      
 16 
     | 
    
         
            +
                          .style("fill", "none");
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              var color = d3.scale
         
     | 
| 
      
 19 
     | 
    
         
            +
                            .ordinal()
         
     | 
| 
      
 20 
     | 
    
         
            +
                            .range(["#428bca", "#5cb85c", "#5bc0de", "#f0ad4e", "#d9534f"]);
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              var arc = d3.svg.arc()
         
     | 
| 
      
 23 
     | 
    
         
            +
                          .outerRadius(radius - 10)
         
     | 
| 
      
 24 
     | 
    
         
            +
                          .innerRadius(radius - 70);
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              var pie = d3.layout.pie()
         
     | 
| 
      
 27 
     | 
    
         
            +
                          .sort(null)
         
     | 
| 
      
 28 
     | 
    
         
            +
                          .value(function(d) { return d.value; });
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              var arcElement = svg.selectAll(".arc")
         
     | 
| 
      
 31 
     | 
    
         
            +
                                  .data(pie(data))
         
     | 
| 
      
 32 
     | 
    
         
            +
                               .enter().append("g")
         
     | 
| 
      
 33 
     | 
    
         
            +
                                  .attr("class", "arc");
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              arcElement.append("path")
         
     | 
| 
      
 36 
     | 
    
         
            +
                        .attr("d", arc)
         
     | 
| 
      
 37 
     | 
    
         
            +
                        .style("fill", function(d) { return color(d.data.name); });
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              arcElement.append("text")
         
     | 
| 
      
 40 
     | 
    
         
            +
                        .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
         
     | 
| 
      
 41 
     | 
    
         
            +
                        .attr("dy", ".35em")
         
     | 
| 
      
 42 
     | 
    
         
            +
                        .style('fill', '#FFFFFF')
         
     | 
| 
      
 43 
     | 
    
         
            +
                        .attr('font-family', 'Arial')
         
     | 
| 
      
 44 
     | 
    
         
            +
                        .attr('font-weight', 'bold')
         
     | 
| 
      
 45 
     | 
    
         
            +
                        .style("text-anchor", "middle")
         
     | 
| 
      
 46 
     | 
    
         
            +
                        .text(function(d) { return d.data.name; });
         
     | 
| 
      
 47 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,111 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            function(data) {
         
     | 
| 
      
 2 
     | 
    
         
            +
              var fns = {
         
     | 
| 
      
 3 
     | 
    
         
            +
                extentOf: function(data, axis) {
         
     | 
| 
      
 4 
     | 
    
         
            +
                  var values    = d3.values(data),
         
     | 
| 
      
 5 
     | 
    
         
            +
                      flatVals  = Array.prototype.concat.apply([], values),
         
     | 
| 
      
 6 
     | 
    
         
            +
                      axisVals  = flatVals.map(function(o) { return o[axis]; });
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  return d3.extent(axisVals);
         
     | 
| 
      
 9 
     | 
    
         
            +
                }
         
     | 
| 
      
 10 
     | 
    
         
            +
              };
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              var margin     = { top: 40, right: 80, bottom: 40, left: 40 },
         
     | 
| 
      
 13 
     | 
    
         
            +
                  width      = 400,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  height     = 400,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  svgWidth   = width + margin.right + margin.left,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  svgHeight  = height + margin.top + margin.bottom;
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              var svg = d3.select("body").append("svg")
         
     | 
| 
      
 19 
     | 
    
         
            +
                         .style("fill", "none")
         
     | 
| 
      
 20 
     | 
    
         
            +
                         .attr("width", svgWidth)
         
     | 
| 
      
 21 
     | 
    
         
            +
                         .attr("height", svgHeight)
         
     | 
| 
      
 22 
     | 
    
         
            +
                         .attr('viewBox', "0 -10 " +svgWidth+ " " +svgHeight+ "")
         
     | 
| 
      
 23 
     | 
    
         
            +
                        .append('g')
         
     | 
| 
      
 24 
     | 
    
         
            +
                          .attr('transform', 'translate(' +margin.left+ ',' +margin.top+ ')')
         
     | 
| 
      
 25 
     | 
    
         
            +
                          .style("fill", "none");
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              var x     = d3.scale
         
     | 
| 
      
 29 
     | 
    
         
            +
                            .linear()
         
     | 
| 
      
 30 
     | 
    
         
            +
                            .range([0, width])
         
     | 
| 
      
 31 
     | 
    
         
            +
                            .domain(fns.extentOf(data, 'x')),
         
     | 
| 
      
 32 
     | 
    
         
            +
                  y     = d3.scale
         
     | 
| 
      
 33 
     | 
    
         
            +
                            .linear()
         
     | 
| 
      
 34 
     | 
    
         
            +
                            .range([height, 0])
         
     | 
| 
      
 35 
     | 
    
         
            +
                            .domain(fns.extentOf(data, 'y')),
         
     | 
| 
      
 36 
     | 
    
         
            +
                  color = d3.scale
         
     | 
| 
      
 37 
     | 
    
         
            +
                            .ordinal()
         
     | 
| 
      
 38 
     | 
    
         
            +
                            .range(["#428bca", "#5cb85c", "#5bc0de", "#f0ad4e", "#d9534f"]);
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              var xAxis  = d3.svg
         
     | 
| 
      
 41 
     | 
    
         
            +
                             .axis()
         
     | 
| 
      
 42 
     | 
    
         
            +
                             .scale(x)
         
     | 
| 
      
 43 
     | 
    
         
            +
                             .orient('bottom')
         
     | 
| 
      
 44 
     | 
    
         
            +
                             .tickSize(-height),
         
     | 
| 
      
 45 
     | 
    
         
            +
                  yAxis  = d3.svg
         
     | 
| 
      
 46 
     | 
    
         
            +
                             .axis()
         
     | 
| 
      
 47 
     | 
    
         
            +
                             .scale(y)
         
     | 
| 
      
 48 
     | 
    
         
            +
                             .orient('left')
         
     | 
| 
      
 49 
     | 
    
         
            +
                             .tickSize(width),
         
     | 
| 
      
 50 
     | 
    
         
            +
                  line   = d3.svg
         
     | 
| 
      
 51 
     | 
    
         
            +
                             .line()
         
     | 
| 
      
 52 
     | 
    
         
            +
                             .interpolate('basis')
         
     | 
| 
      
 53 
     | 
    
         
            +
                             .x(function(d) { return x(d.x); })
         
     | 
| 
      
 54 
     | 
    
         
            +
                             .y(function(d) { return y(d.y); });
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              var xAxisElement = svg.append('g')
         
     | 
| 
      
 57 
     | 
    
         
            +
                                 .attr('class',      'x axis')
         
     | 
| 
      
 58 
     | 
    
         
            +
                                 .attr('transform',  "translate(0," +height+ ")")
         
     | 
| 
      
 59 
     | 
    
         
            +
                                 .call(xAxis);
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              var yAxisElement = svg.append('g')
         
     | 
| 
      
 62 
     | 
    
         
            +
                                 .attr('class', 'y axis')
         
     | 
| 
      
 63 
     | 
    
         
            +
                                 .attr("transform", "translate(" +width+ ",0)")
         
     | 
| 
      
 64 
     | 
    
         
            +
                                 .call(yAxis);
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              var lineElements = svg.selectAll('.line')
         
     | 
| 
      
 67 
     | 
    
         
            +
                                   .data(d3.entries(data))
         
     | 
| 
      
 68 
     | 
    
         
            +
                                 .enter().append('g')
         
     | 
| 
      
 69 
     | 
    
         
            +
                                   .attr('class', 'line');
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              lineElements.append('path')
         
     | 
| 
      
 72 
     | 
    
         
            +
                .attr('class', 'line')
         
     | 
| 
      
 73 
     | 
    
         
            +
                .attr('stroke-width', '2px')
         
     | 
| 
      
 74 
     | 
    
         
            +
                .attr('opacity',  '0.3')
         
     | 
| 
      
 75 
     | 
    
         
            +
                .attr('d', function(d) { return line(d.value) })
         
     | 
| 
      
 76 
     | 
    
         
            +
                .attr('stroke', function(d) { return color(d.key); });
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
              lineElements.append('text')
         
     | 
| 
      
 79 
     | 
    
         
            +
                .datum(function(d) { return { name: d.key, value: d.value[d.value.length - 1] }; })
         
     | 
| 
      
 80 
     | 
    
         
            +
                .attr('transform', function(d) { return "translate(" +x(d.value.x)+ "," +y(d.value.y)+ ")"; })
         
     | 
| 
      
 81 
     | 
    
         
            +
                .attr('x', 3)
         
     | 
| 
      
 82 
     | 
    
         
            +
                .attr('dy', '.35em')
         
     | 
| 
      
 83 
     | 
    
         
            +
                .style('fill', '#333333')
         
     | 
| 
      
 84 
     | 
    
         
            +
                .attr('font-family', 'Arial')
         
     | 
| 
      
 85 
     | 
    
         
            +
                .text(function(d) { return d.name; });
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              // axis styling
         
     | 
| 
      
 88 
     | 
    
         
            +
              xAxisElement.selectAll('line')
         
     | 
| 
      
 89 
     | 
    
         
            +
                .style('fill', 'none')
         
     | 
| 
      
 90 
     | 
    
         
            +
                .attr('stroke', 'black')
         
     | 
| 
      
 91 
     | 
    
         
            +
                .attr('stroke-width', '1px')
         
     | 
| 
      
 92 
     | 
    
         
            +
                .attr('stroke-opacity', 0.1)
         
     | 
| 
      
 93 
     | 
    
         
            +
                .attr('shape-rendering', 'geometricPrecision');
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
              xAxisElement.selectAll('text')
         
     | 
| 
      
 96 
     | 
    
         
            +
                .style('fill', '#333333')
         
     | 
| 
      
 97 
     | 
    
         
            +
                .attr('font-family', 'Arial')
         
     | 
| 
      
 98 
     | 
    
         
            +
                .attr('dy', '20px');
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              yAxisElement.selectAll('line')
         
     | 
| 
      
 101 
     | 
    
         
            +
                .style('fill', 'none')
         
     | 
| 
      
 102 
     | 
    
         
            +
                .attr('stroke', 'black')
         
     | 
| 
      
 103 
     | 
    
         
            +
                .attr('stroke-width', '1px')
         
     | 
| 
      
 104 
     | 
    
         
            +
                .attr('stroke-opacity', 0.1)
         
     | 
| 
      
 105 
     | 
    
         
            +
                .attr('shape-rendering', 'geometricPrecision');
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              yAxisElement.selectAll('text')
         
     | 
| 
      
 108 
     | 
    
         
            +
                .style('fill', '#333333')
         
     | 
| 
      
 109 
     | 
    
         
            +
                .attr('font-family', 'Arial')
         
     | 
| 
      
 110 
     | 
    
         
            +
                .attr('dx', '-10px');
         
     | 
| 
      
 111 
     | 
    
         
            +
            }
         
     | 
    
        data/lib/grapht.rb
    ADDED
    
    
    
        data/lib/grapht/shell.rb
    ADDED
    
    | 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grapht
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Shell
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Error < StandardError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                CMD = File.join(Grapht::ROOT, 'bin/grapht')
         
     | 
| 
      
 8 
     | 
    
         
            +
                ALLOWED_OPTIONS  = %w(-f)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def self.exec(type, json_data, options={})
         
     | 
| 
      
 11 
     | 
    
         
            +
                  options = *options.select { |k,v| ALLOWED_OPTIONS.include? k }.flatten
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  out, err, status = Open3.capture3 CMD, type, *options, stdin_data: json_data
         
     | 
| 
      
 14 
     | 
    
         
            +
                  raise Grapht::Shell::Error, err unless status.success?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  out
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/grapht/type.rb
    ADDED
    
    
| 
         @@ -0,0 +1,107 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            page          = require('webpage').create()
         
     | 
| 
      
 2 
     | 
    
         
            +
            system        = require('system')
         
     | 
| 
      
 3 
     | 
    
         
            +
            fs            = require('fs')
         
     | 
| 
      
 4 
     | 
    
         
            +
            thisFile      = system.args[0]
         
     | 
| 
      
 5 
     | 
    
         
            +
            graphType     = system.args[1]
         
     | 
| 
      
 6 
     | 
    
         
            +
            graphFormat   = false
         
     | 
| 
      
 7 
     | 
    
         
            +
            scriptPath    = fs.absolute(thisFile)
         
     | 
| 
      
 8 
     | 
    
         
            +
                              .replace(/[\w\s\-\.]+?\.(js|coffee)$/i, '')
         
     | 
| 
      
 9 
     | 
    
         
            +
            vendorPath    = "#{scriptPath}../vendor/"
         
     | 
| 
      
 10 
     | 
    
         
            +
            defsPath      = "#{scriptPath}../lib/graph-definitions/"
         
     | 
| 
      
 11 
     | 
    
         
            +
            userDefsPath  = system.env['EXT_GRAPHT_DEFINITIONS_HOME']
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies  = ['d3.min.js', 'json2.js']
         
     | 
| 
      
 13 
     | 
    
         
            +
            niceDirPathRX = /\/$|$/
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            # -----------------------------------------------------------------------------
         
     | 
| 
      
 16 
     | 
    
         
            +
            # Helper Functions
         
     | 
| 
      
 17 
     | 
    
         
            +
            # -----------------------------------------------------------------------------
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            fns =
         
     | 
| 
      
 20 
     | 
    
         
            +
              # Logs the supplied message, and optional trace to STDERR and exits the process
         
     | 
| 
      
 21 
     | 
    
         
            +
              # with an exit code of 1.
         
     | 
| 
      
 22 
     | 
    
         
            +
              logError: (message, trace) ->
         
     | 
| 
      
 23 
     | 
    
         
            +
                fs.write '/dev/stderr', "<ERROR> #{message}\n"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                if trace
         
     | 
| 
      
 26 
     | 
    
         
            +
                  traceString = trace.map (t) -> "\t#{t.file}: on line: #{t.line}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  fs.write '/dev/stderr', traceString.join('\n')
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                phantom.exit(1)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              # Searchs for a valid graph definition in the supplied definition paths.  If
         
     | 
| 
      
 32 
     | 
    
         
            +
              # no definition is found, we log an error to STDERR and exit with an exitcode
         
     | 
| 
      
 33 
     | 
    
         
            +
              # of 1.
         
     | 
| 
      
 34 
     | 
    
         
            +
              findDef: (type, defPaths...) ->
         
     | 
| 
      
 35 
     | 
    
         
            +
                for dir in defPaths when dir?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  dir   = dir.replace(niceDirPathRX, '/')
         
     | 
| 
      
 37 
     | 
    
         
            +
                  path  = "#{dir}#{type}.js"
         
     | 
| 
      
 38 
     | 
    
         
            +
                  return path if fs.exists(path)
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                @logError "No graph definition could be found for '#{type}'"
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              loadDef: (def) -> fs.read(def)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              # Wraps the supplied graph definition in a function that executes the definition
         
     | 
| 
      
 45 
     | 
    
         
            +
              # and returns the resulting content of the document body.  This function is intended
         
     | 
| 
      
 46 
     | 
    
         
            +
              # to minimize boiler-plate in graph definitions, and reduce the likelihood of user
         
     | 
| 
      
 47 
     | 
    
         
            +
              # error.
         
     | 
| 
      
 48 
     | 
    
         
            +
              wrapDef: (def) ->
         
     | 
| 
      
 49 
     | 
    
         
            +
                "function() {
         
     | 
| 
      
 50 
     | 
    
         
            +
                  (#{def}).apply(this, arguments);
         
     | 
| 
      
 51 
     | 
    
         
            +
                  return document.body.innerHTML;
         
     | 
| 
      
 52 
     | 
    
         
            +
                }"
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              getOptions: ->
         
     | 
| 
      
 55 
     | 
    
         
            +
                optionsIn   = system.args[2..]
         
     | 
| 
      
 56 
     | 
    
         
            +
                optionsOut  = {}
         
     | 
| 
      
 57 
     | 
    
         
            +
                slice       = 2
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                for i in [0...optionsIn.length] by slice
         
     | 
| 
      
 60 
     | 
    
         
            +
                  [key, value]    = optionsIn[i...i+slice]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  optionsOut[key] = value
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                optionsOut
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              getFormat: ->
         
     | 
| 
      
 66 
     | 
    
         
            +
                options = @getOptions()
         
     | 
| 
      
 67 
     | 
    
         
            +
                options['-f'] || options['--format']
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              readDataIn: ->
         
     | 
| 
      
 70 
     | 
    
         
            +
                try
         
     | 
| 
      
 71 
     | 
    
         
            +
                  if fs.size('/dev/stdin') == 0
         
     | 
| 
      
 72 
     | 
    
         
            +
                    fns.logError('No graph data was received!')
         
     | 
| 
      
 73 
     | 
    
         
            +
                  else
         
     | 
| 
      
 74 
     | 
    
         
            +
                    fs.read('/dev/stdin')
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                catch err
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @logError err
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            # -----------------------------------------------------------------------------
         
     | 
| 
      
 81 
     | 
    
         
            +
            # Core Graph Generation Logic
         
     | 
| 
      
 82 
     | 
    
         
            +
            # -----------------------------------------------------------------------------
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            # Configure the page context.
         
     | 
| 
      
 85 
     | 
    
         
            +
            page.libraryPath = vendorPath
         
     | 
| 
      
 86 
     | 
    
         
            +
            page.onError     = fns.logError
         
     | 
| 
      
 87 
     | 
    
         
            +
            dependencies.forEach (dp) -> page.injectJs(dp) || Helper.logError "could not load #{dp}!"
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            # load and evaluate the graph definition within the context of the JSON, supplied
         
     | 
| 
      
 90 
     | 
    
         
            +
            # via STDIN.
         
     | 
| 
      
 91 
     | 
    
         
            +
            graphData   = fns.readDataIn()
         
     | 
| 
      
 92 
     | 
    
         
            +
            graphDef    = fns.wrapDef fns.loadDef fns.findDef(graphType, userDefsPath, defsPath)
         
     | 
| 
      
 93 
     | 
    
         
            +
            graphFormat = fns.getFormat()
         
     | 
| 
      
 94 
     | 
    
         
            +
            parsedData  = try
         
     | 
| 
      
 95 
     | 
    
         
            +
                            JSON.parse(graphData)
         
     | 
| 
      
 96 
     | 
    
         
            +
                          catch err
         
     | 
| 
      
 97 
     | 
    
         
            +
                            fns.logError(err)
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            page.content = content = page.evaluate(graphDef, parsedData)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
            # Write resulting content to STDOUT and exit.
         
     | 
| 
      
 102 
     | 
    
         
            +
            if graphFormat
         
     | 
| 
      
 103 
     | 
    
         
            +
              page.render '/dev/stdout', { format: graphFormat, quality: 100 }
         
     | 
| 
      
 104 
     | 
    
         
            +
            else
         
     | 
| 
      
 105 
     | 
    
         
            +
              fs.write '/dev/stdout', "#{content}\n"
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            phantom.exit()
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Grapht::Shell do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe '.exec' do
         
     | 
| 
      
 5 
     | 
    
         
            +
                let(:type)       { Grapht::Type::BAR_HORIZONTAL }
         
     | 
| 
      
 6 
     | 
    
         
            +
                let(:format)     { "" }
         
     | 
| 
      
 7 
     | 
    
         
            +
                let(:json_data)  {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  <<-json
         
     | 
| 
      
 9 
     | 
    
         
            +
                  [
         
     | 
| 
      
 10 
     | 
    
         
            +
                    { "name": "foo", "value": 20 },
         
     | 
| 
      
 11 
     | 
    
         
            +
                    { "name": "bar", "value": 40 },
         
     | 
| 
      
 12 
     | 
    
         
            +
                    { "name": "baz", "value": 35 }
         
     | 
| 
      
 13 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  json
         
     | 
| 
      
 15 
     | 
    
         
            +
                }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                subject { Grapht::Shell.exec type, json_data, '-f' => format }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                context 'when given a known type' do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  it { should match(/^<svg/) }
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                context 'when given an unknown type' do
         
     | 
| 
      
 24 
     | 
    
         
            +
                  let(:type) { 'some-invalid-graph-type' }
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  it "should raise a Grapht::Shell:Error" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                    expect { subject }.to raise_error(Grapht::Shell::Error, /No graph definition could be found/)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                context 'when well-formed data is provided' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  it { should match(/^<svg/) }
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                context 'when malformed data is provided' do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  let(:json_data) { "{} <this is not JSON>" }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  it "should raise a Grapht::Shell::Error" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                    expect { subject }.to raise_error(Grapht::Shell::Error, /Unable to parse JSON string/)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                context 'when no data is provided' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  let(:json_data) { "" }
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  it "should raise a Grapht::Shell::Error" do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    expect { subject }.to raise_error(Grapht::Shell::Error, /No graph data was received/)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                context 'when no format is provided' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  it { should match(/^<svg/) }
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                context 'when an unknown format is provided' do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  let(:format) { 'some-crazy-invalid-format' }
         
     | 
| 
      
 57 
     | 
    
         
            +
                  it { should be_empty }
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                context "when a binary format is provided" do
         
     | 
| 
      
 61 
     | 
    
         
            +
                  before { subject.force_encoding('binary') }
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  context "when a format of 'png' is provided" do
         
     | 
| 
      
 64 
     | 
    
         
            +
                    let(:format) { 'png' }
         
     | 
| 
      
 65 
     | 
    
         
            +
                    it { should start_with("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A".force_encoding('binary')) }
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  context "when a format of 'jpg' is provided" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                    let(:format) { 'jpg' }
         
     | 
| 
      
 70 
     | 
    
         
            +
                    it { should start_with("\xFF\xD8\xFF\xE0\x00\x10JFIF\x00".force_encoding('binary')) }
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  context "when a format of 'gif' is provided" do
         
     | 
| 
      
 74 
     | 
    
         
            +
                    let(:format) { 'gif' }
         
     | 
| 
      
 75 
     | 
    
         
            +
                    it { should start_with("\x47\x49\x46\x38\x37\x61".force_encoding('binary')) }
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     |