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.
@@ -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
+ }