grapht 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e6bffdc5a59f292991c12f400d7bf92513c50aca
4
+ data.tar.gz: a0059941f096ba157c90d29cc32e16f1b6370861
5
+ SHA512:
6
+ metadata.gz: d5a1db3b4f08a414f1d8b8c1399fe2c89cbe475db450d06a7b137528b1985ee0053d4029c3a22685a58d450899e34e124325ba136dfb8b4e8890e46ab3ab5d73
7
+ data.tar.gz: 8f79d9688f7f91370d5f4a5f8f0306230919d2957fc30c7e9cf9d5e67af3dd40be9f492d5075d297822df290e836911e9a9d930080113560b02e79ef96152fea
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .DS_Store
20
+ profiles/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --warnings
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.0.0'
3
+
4
+ group :test do
5
+ gem 'rspec'
6
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Trade Informatics Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,267 @@
1
+ <img src='http://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Graft_182_%28PSF%29.png/320px-Graft_182_%28PSF%29.png' style='width: 320px; margin: 0 auto; display: block;'/>
2
+
3
+ # Grapht
4
+
5
+ Grapht is a server-side graphing library built on [PhantomJS](https://github.com/ariya/phantomjs/wiki)
6
+ and utilizing [D3.js](http://d3js.org/). Grapht provides a CLI for simple Bash scripting.
7
+ It also profides a light-weight [Ruby](https://www.ruby-lang.org/en/)
8
+ API to make service-level integration simple.
9
+
10
+ ### Why was Grapht built on PhantomJS?
11
+
12
+ - PhantomJS allows us to leverage [D3.js](http://d3js.org/), a best-of-breed data visualization library
13
+ authored by the formidable data visualization expert, Mike Bostock. [D3.js](http://d3js.org/) is a
14
+ battlefield tested library.
15
+ - Using PhantomJS allows us to reuse existing data visualization logic, originally
16
+ authored for our client-side application. This means we get consistent visualizations
17
+ across the various layers of our stack, and we minimize developer effort.
18
+
19
+ ### How well does Grapht perform?
20
+
21
+ While PhantomJS is able to run Javascript extremely fast, it has a slow startup
22
+ time. When measured on a 2.4GHz Intel Core i7 laptop, with 16GB of DDR3 RAM, the
23
+ benchmarks<sup>†</sup> are as follows (_averaged over 10 runs_):
24
+
25
+ <table style='width: 100%;'>
26
+ <thead>
27
+ <tr>
28
+ <th>Bytes-In</th>
29
+ <th>Average, Real Time <small style='color:gray; font-weight:lighter;'>( Seconds )</small></th>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ <tr>
34
+ <td>1,323</td>
35
+ <td>1.559</td>
36
+ </tr>
37
+ <tr>
38
+ <td>13,527</td>
39
+ <td>1.675</td>
40
+ </tr>
41
+ <tr>
42
+ <td>135,252</td>
43
+ <td>1.715</td>
44
+ </tr>
45
+ </tbody>
46
+ </table>
47
+
48
+ <div style='width: 320px; margin: 0 auto;'>
49
+ <img src='http://i.imgur.com/NyhWdxx.jpg'/>
50
+ <p>
51
+ <small style='color:gray;'><em>time (sec.) / bytes</em></small>
52
+ </p>
53
+ </div>
54
+
55
+ Note that even when incrementing the amount of data-in by an order of magnitude,
56
+ time increases minimally (_roughly √n_). From this, we can infer a start-up time of ~1.5 seconds,
57
+ for PhantomJS, on the aforementioned hardware.
58
+
59
+ <sup>†</sup> _All measurements were collected using the following command: `time -p bin/grapht bar-horizontal < data/bar_data.json > /dev/null`_
60
+
61
+ ## CLI
62
+
63
+ ### Overview
64
+ Grapht provides a CLI, accessed using the `bin/grapht` command. The basic invocation
65
+ requires one argument specifying the desired graph type, and a JSON string provided
66
+ via `STDIN`. For example, if we want a **horizontal bar graph**:
67
+
68
+ bin/grapht bar-horizontal < ~/my-data.json
69
+
70
+ The result will be a string of **svg** markup:
71
+
72
+ <svg width="200" height="200">
73
+ <rect class="bar" x="0" y="13" width="0" height="50"></rect>
74
+ <rect class="bar" x="0" y="75" width="200" height="50"></rect>
75
+ <rect class="bar" x="0" y="137" width="150" height="50"></rect>
76
+ <g class="x axis" transform="translate(0,200)">
77
+ <g class="tick" style="opacity: 1; " transform="translate(0,0)">
78
+ <line y2="-200" x2="0"></line>
79
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">20</text>
80
+ </g>
81
+ <g class="tick" style="opacity: 1; " transform="translate(20,0)">
82
+ <line y2="-200" x2="0"></line>
83
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">22</text>
84
+ </g>
85
+ <g class="tick" style="opacity: 1; " transform="translate(40,0)">
86
+ <line y2="-200" x2="0"></line>
87
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">24</text>
88
+ </g>
89
+ <g class="tick" style="opacity: 1; " transform="translate(60.00000000000001,0)">
90
+ <line y2="-200" x2="0"></line>
91
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">26</text>
92
+ </g>
93
+ <g class="tick" style="opacity: 1; " transform="translate(80,0)">
94
+ <line y2="-200" x2="0"></line>
95
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">28</text>
96
+ </g>
97
+ <g class="tick" style="opacity: 1; " transform="translate(100,0)">
98
+ <line y2="-200" x2="0"></line>
99
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">30</text>
100
+ </g>
101
+ <g class="tick" style="opacity: 1; " transform="translate(120.00000000000001,0)">
102
+ <line y2="-200" x2="0"></line>
103
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">32</text>
104
+ </g>
105
+ <g class="tick" style="opacity: 1; " transform="translate(140,0)">
106
+ <line y2="-200" x2="0"></line>
107
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">34</text>
108
+ </g>
109
+ <g class="tick" style="opacity: 1; " transform="translate(160,0)">
110
+ <line y2="-200" x2="0"></line>
111
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">36</text>
112
+ </g>
113
+ <g class="tick" style="opacity: 1; " transform="translate(180,0)">
114
+ <line y2="-200" x2="0"></line>
115
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">38</text>
116
+ </g>
117
+ <g class="tick" style="opacity: 1; " transform="translate(200,0)">
118
+ <line y2="-200" x2="0"></line>
119
+ <text y="3" x="0" dy=".71em" style="text-anchor: middle; ">40</text>
120
+ </g>
121
+ <path class="domain" d="M0,-200V0H200V-200"></path>
122
+ </g>
123
+ <g class="y axis">
124
+ <g class="tick" style="opacity: 1; " transform="translate(0,38)">
125
+ <line x2="6" y2="0"></line>
126
+ <text x="9" y="0" dy=".32em" style="text-anchor: start; ">foo</text>
127
+ </g>
128
+ <g class="tick" style="opacity: 1; " transform="translate(0,100)">
129
+ <line x2="6" y2="0"></line>
130
+ <text x="9" y="0" dy=".32em" style="text-anchor: start; ">bar</text>
131
+ </g>
132
+ <g class="tick" style="opacity: 1; " transform="translate(0,162)">
133
+ <line x2="6" y2="0"></line>
134
+ <text x="9" y="0" dy=".32em" style="text-anchor: start; ">baz</text>
135
+ </g>
136
+ <path class="domain" d="M6,0H0V200H6"></path>
137
+ </g>
138
+ </svg>
139
+
140
+ ### Usage
141
+
142
+ bin/grapht GRAPH_TYPE [options] < JSON_INPUT
143
+
144
+ ### Options
145
+
146
+ Currently supported flags:
147
+
148
+ <table style='width: 100%;'>
149
+ <thead>
150
+ <tr>
151
+ <th>Flag</th>
152
+ <th>Description</th>
153
+ <th>Allowable Values</th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>
157
+ <tr>
158
+ <td><code>-f</code></td>
159
+ <td>Specifies an output format for the generated graph.</td>
160
+ <td><code>png|jpg|gif|pdf</code></td>
161
+ </tr>
162
+ </tbody>
163
+ </table>
164
+
165
+ ## Ruby API
166
+
167
+ To generate the same **horizontal bar graph** generated in the CLI
168
+ section--using the Ruby API--we can do the following:
169
+
170
+ json = "[{ \"name\": \"foo\", \"value\": 20 },{ \"name\": \"bar\", \"value\": 40 },{ \"name\": \"baz\", \"value\": 35 }]"
171
+ type = Grapht::Type::BAR_HORIZONTAL
172
+ graph = Grapht::Shell.exec type, json
173
+
174
+ ## Supported Graphs
175
+
176
+ Grapht is extensible, allowing users to create new graph definitions using
177
+ Javascript and [D3.js](http://d3js.org). However, Grapht provides a handful
178
+ of graph definitions out of the box:
179
+
180
+ - Horizontal Bar Graph
181
+ - Vertical Bar Graph
182
+ - Line Graph
183
+ - Pie Graph
184
+
185
+ ### User Defined Graph Definitions
186
+
187
+ Users may create their own graph definitions with Grapht. To do this, we first
188
+ have to register a location where the new graph definitions will reside. This
189
+ is done simply by setting the `EXT_GRAPHT_DEFINITIONS_HOME` environment variable.
190
+ For example, if we have our custom definitions stored in
191
+ `~/Development/my_project/my_graph_defs`, we set our environment variable to this
192
+ path:
193
+
194
+ export EXT_GRAPHT_DEFINITIONS_HOME=~/Development/my_project/my_graph_defs
195
+ bin/grapht my-scatterplot < ~/my-data.json
196
+
197
+ In the example above, we supply the name of our new graph definition
198
+ (`my-scatterplot`) to the Grapht CLI. Grapht will first look in
199
+ `EXT_GRAPHT_DEFINITIONS_HOME` for the file `my-scatterplot.js`. If it's not
200
+ found, it will check its own internal set of graph definitions. If it is found,
201
+ Grapht will load the new definition.
202
+
203
+ ##### Creating Your First Graph Definition
204
+
205
+ All graph definitions must comply with a short set of rules:
206
+
207
+ - They must be wrapped within a function that takes a single data argument.
208
+ - They must generate one, and only one root DOM node.
209
+
210
+ Here's an example of a valid graph definition:
211
+
212
+ function(data) { // <- our wrapper fuction
213
+ var width = 200,
214
+ height = 200;
215
+
216
+ // our single root node added to document.body
217
+ var svg = d3.select("body").append("svg")
218
+ .attr("width", width)
219
+ .attr("height", height);
220
+
221
+ // a bunch of D3 operations on svg...
222
+ }
223
+
224
+ ## Error Handling
225
+
226
+ Grapht will raise an error in the following scenarios:
227
+
228
+ - When any of its dependencies cannot be found. _Currently, Grapht
229
+ depends upon the D3.js and JSON2 javascript libraries. These dependencies
230
+ can be found in Grapht's `vendor` directory._
231
+ - When an unknown graph type is supplied to Grapht
232
+ - When malformed JSON is supplied to Grapht
233
+ - When PhantomJS raises an internal error
234
+
235
+ When using Grapht's CLI, all errors are printed to `STDERR` and Grapht exits
236
+ with an exit-code of `1`.
237
+
238
+ When accessing Grapht's features through the Ruby API, all errors are presented
239
+ as instances of `Grapht::Shell::Error`. The error messages, in this case, are
240
+ consistent with the errors messages raised from the CLI.
241
+
242
+ ## Rails Integration
243
+
244
+ While Rails integration is not scripted by the Grapht library, it is quite
245
+ simple to integrate. The following steps are all that is required for integration:
246
+
247
+ - Add the following to your `Gemfile`:
248
+ <pre>gem 'grapht', git: 'https://github.com/ibpinc/grapht.git'</pre>
249
+ - In the Rails `app` directory, create a directory named `graph-definitions`
250
+ - Create the file `config/initializers/grapht.rb`. In the file add the following:
251
+ <pre>ENV['EXT_GRAPHT_DEFINITIONS_HOME'] =
252
+ Rails.root.join('app/graph-definitions')</pre>
253
+ - Place all user-defined graph definitions in the `app/graph-definitions`
254
+
255
+ ## Contributing
256
+
257
+ 1. Fork it (http://github.com/ibpinc/grapht/fork)
258
+ 2. Create your feature branch `git checkout -b my-new-feature`
259
+ 3. Commit your changes `git commit -am 'Add some feature'`
260
+ 4. Push to the branch `git push origin my-new-feature`
261
+ 5. Create a new pull request
262
+
263
+ ## License
264
+
265
+ Copyright (c) 2014 Trade Informatics, Inc
266
+
267
+ [MIT License](https://github.com/ibpinc/grapht/blob/master/LICENSE.txt)
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ script="${BASH_SOURCE%/*}/../script/grapht.coffee"
3
+
4
+ phantom_version=`phantomjs -v`
5
+ if [ $? -ne 0 ]; then
6
+ echo "could not find phantomjs!"
7
+ exit 1
8
+ fi
9
+
10
+ if [ "$#" -ge 1 ]; then
11
+ phantomjs $script $@ < /dev/stdin
12
+ else
13
+ echo "ERROR:"
14
+ echo "-- grapht was called with unknown arguments:"
15
+ echo "-- grapht requires an initial argument, specifying the graph type."
16
+ exit 1
17
+ fi
@@ -0,0 +1,5 @@
1
+ [
2
+ { "name": "foo", "value": 20 },
3
+ { "name": "bar", "value": 40 },
4
+ { "name": "baz", "value": 35 }
5
+ ]
@@ -0,0 +1,5 @@
1
+ [
2
+ { "name": "foo", "value": 20 },
3
+ { "name": "bar", "value": 40 },
4
+ { "name": "baz", "value": 35 }
5
+ ]
@@ -0,0 +1,7 @@
1
+ {
2
+ "perf": [
3
+ { "x": 1323, "y": 1.559 },
4
+ { "x": 13527, "y": 1.675 },
5
+ { "x": 135252, "y": 1.715 }
6
+ ]
7
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "london": [
3
+ { "x": 0, "y": 10 },
4
+ { "x": 10, "y": 0 },
5
+ { "x": 20, "y": 20 },
6
+ { "x": 30, "y": 40 },
7
+ { "x": 40, "y": 60 }
8
+ ],
9
+ "paris": [
10
+ { "x": 0, "y": 80 },
11
+ { "x": 10, "y": 60 },
12
+ { "x": 20, "y": 40 },
13
+ { "x": 30, "y": 20 },
14
+ { "x": 40, "y": 0 }
15
+ ]
16
+ }
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grapht/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grapht"
8
+ spec.version = Grapht::VERSION
9
+ spec.authors = ["Tim Lowrimore"]
10
+ spec.email = ["tim@plia.com"]
11
+ spec.description = %q{Grapht is a server-side graphing library built on PhantomJS and utilizing D3.js. Grapht provides a CLI for simple Bash scripting. It also profides a light-weight Ruby API to make service-level integration simple.}
12
+ spec.summary = %q{Grapht is a server-side graphing library built on PhantomJS and utilizing D3.js.}
13
+ spec.homepage = "https://github.com/ibpinc/grapht"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,90 @@
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
+ var paddedExtent = function(data, pad) {
18
+ return [
19
+ d3.min(data, function(d) { return d.value-pad; }),
20
+ d3.max(data, function(d) { return d.value+pad; })
21
+ ]
22
+ }
23
+
24
+ // Axis Scales
25
+ var x = d3.scale
26
+ .linear()
27
+ .domain(paddedExtent(data, 1))
28
+ .range([0, width])
29
+ .nice();
30
+
31
+ var y = d3.scale
32
+ .ordinal()
33
+ .domain(data.map(function(d) { return d.name; }))
34
+ .rangeRoundBands([0, height], 0.2);
35
+
36
+ // Axes
37
+ var xAxis = d3.svg
38
+ .axis()
39
+ .scale(x)
40
+ .orient('bottom')
41
+ .tickSize(-height);
42
+
43
+ var yAxis = d3.svg
44
+ .axis()
45
+ .scale(y)
46
+ .orient('right')
47
+ .ticks(data.length);
48
+
49
+ svg.selectAll('.bar')
50
+ .data(data)
51
+ .enter().append('rect')
52
+ .attr('class', 'bar')
53
+ .style('fill', '#428bca')
54
+ .attr('opacity', '0.3')
55
+ .attr('x', 0)
56
+ .attr('y', function(d) { return y(d.name); })
57
+ .attr('width', function(d) { return x(d.value); })
58
+ .attr('height', y.rangeBand());
59
+
60
+ svg.selectAll('.text')
61
+ .attr('font-family', 'Arial')
62
+ .selectAll('label')
63
+ .attr('fill-opacity', '0.3')
64
+
65
+ var xAxisElement = svg.append('g')
66
+ .attr('class', 'x axis')
67
+ .style("fill", "none")
68
+ .attr('transform', "translate(0," +height+ ")")
69
+ .call(xAxis);
70
+
71
+ var yAxisElement = svg.append('g')
72
+ .attr('class', 'y axis')
73
+ .style("fill", "none")
74
+ .call(yAxis);
75
+
76
+ xAxisElement.selectAll('line')
77
+ .style('fill', 'none')
78
+ .attr('stroke', 'black')
79
+ .attr('stroke-width', '1px')
80
+ .attr('stroke-opacity', 0.1)
81
+ .attr('shape-rendering', 'geometricPrecision');
82
+
83
+ xAxisElement.selectAll('text')
84
+ .style('fill', '#333333')
85
+ .attr('font-family', 'Arial');
86
+
87
+ yAxisElement.selectAll('text')
88
+ .style('fill', '#333333')
89
+ .attr('font-family', 'Arial');
90
+ }