gremlin2dot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +28 -0
- data/README.md +6 -0
- data/bin/gremlin2dot +84 -0
- data/lib/gremlin2dot/data_model.rb +60 -0
- data/lib/gremlin2dot/graph_builder.rb +54 -0
- data/lib/gremlin2dot/unmangler.rb +67 -0
- data/lib/gremlin2dot.rb +3 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 329541aa877df7ee19cd655d44becf877b32739d76b41dfd1b5a97b3aad1f619
|
4
|
+
data.tar.gz: 074615b05287eda09c507586b86891bf30d1f34cf113e6b031ae7906550fe60c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51e50cc7cd8e90042a07beb665665073f9aa626056c52b40865eb92b1fbdd1e1c57a8dfaa3bf0eec7ea77fe68caf4c260b711e2f6e94a5096b8336145f1f64b6
|
7
|
+
data.tar.gz: 77e8a9450f43d4b50a0540c18695991b7b4a319b7a9efb0d23bd987c71034572518c04679f7347de8e4e7ded6b50da315b9e204f887f8536b99c9c13e36b729f
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
GEM
|
2
|
+
specs:
|
3
|
+
domain_name (0.5.20180417)
|
4
|
+
unf (>= 0.0.5, < 1.0.0)
|
5
|
+
http-cookie (1.0.3)
|
6
|
+
domain_name (~> 0.5)
|
7
|
+
mime-types (3.1)
|
8
|
+
mime-types-data (~> 3.2015)
|
9
|
+
mime-types-data (3.2016.0521)
|
10
|
+
netrc (0.11.0)
|
11
|
+
rest-client (2.0.2)
|
12
|
+
http-cookie (>= 1.0.2, < 2.0)
|
13
|
+
mime-types (>= 1.16, < 4.0)
|
14
|
+
netrc (~> 0.8)
|
15
|
+
ruby-graphviz (1.2.2)
|
16
|
+
unf (0.1.4)
|
17
|
+
unf_ext
|
18
|
+
unf_ext (0.0.7.5)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
rest-client (~> 2.0)
|
25
|
+
ruby-graphviz (~> 1.0)
|
26
|
+
|
27
|
+
BUNDLED WITH
|
28
|
+
1.16.1
|
data/README.md
ADDED
data/bin/gremlin2dot
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vi: set ts=2 sw=2 :
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def error(message)
|
7
|
+
$stderr.puts message
|
8
|
+
exit 2
|
9
|
+
end
|
10
|
+
|
11
|
+
endpoint = nil
|
12
|
+
query = nil
|
13
|
+
use_stdin = false
|
14
|
+
|
15
|
+
json_file = nil
|
16
|
+
|
17
|
+
canon_file = nil
|
18
|
+
dot_file = nil
|
19
|
+
png_file = nil
|
20
|
+
svg_file = nil
|
21
|
+
|
22
|
+
opts = OptionParser.new
|
23
|
+
opts.on('-e', '--endpoint URL', 'The URL of the Gremlin endpoint') {|v| endpoint = v }
|
24
|
+
opts.on('-q', '--query QUERY', 'The Gremlin query to run') {|v| query = v }
|
25
|
+
opts.on('--stdin', 'Read Gremlin results as JSON from standard input') { use_stdin = true }
|
26
|
+
opts.on('--save-json FILE', 'Save the JSON to FILE') {|v| json_file = v }
|
27
|
+
opts.on('--canon FILE', 'Write the results as canonical DOT to FILE') {|v| canon_file = v }
|
28
|
+
opts.on('--dot FILE', 'Write the results as placed DOT to FILE') {|v| dot_file = v }
|
29
|
+
opts.on('--png FILE', 'Write the results as PNG to FILE') {|v| png_file = v }
|
30
|
+
opts.on('--svg FILE', 'Write the results as SVG to FILE') {|v| svg_file = v }
|
31
|
+
opts.order!
|
32
|
+
|
33
|
+
if !canon_file and !dot_file and !png_file and !svg_file
|
34
|
+
canon_file = "/dev/stdout"
|
35
|
+
end
|
36
|
+
|
37
|
+
if endpoint.nil? != query.nil?
|
38
|
+
error "Either specify both --endpoint and --query, or specify --stdin 1"
|
39
|
+
end
|
40
|
+
|
41
|
+
if use_stdin != endpoint.nil?
|
42
|
+
error "Either specify both --endpoint and --query, or specify --stdin 2"
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'json'
|
46
|
+
require 'gremlin2dot'
|
47
|
+
require 'ruby-graphviz'
|
48
|
+
|
49
|
+
json_text = if use_stdin
|
50
|
+
$stdin.read
|
51
|
+
else
|
52
|
+
require 'rest-client'
|
53
|
+
RestClient.post(
|
54
|
+
endpoint,
|
55
|
+
{ gremlin: query }.to_json,
|
56
|
+
{ content_type: :json, accept: :json },
|
57
|
+
).body
|
58
|
+
end
|
59
|
+
|
60
|
+
if json_file
|
61
|
+
IO.write(json_file, json_text)
|
62
|
+
end
|
63
|
+
|
64
|
+
query_results = JSON.parse(json_text)
|
65
|
+
unmangled_data = Gremlin2Dot::Unmangler.new.unmangle(query_results)
|
66
|
+
g = Gremlin2Dot::GraphBuilder.build(unmangled_data)
|
67
|
+
|
68
|
+
if canon_file
|
69
|
+
g.output(canon: canon_file)
|
70
|
+
end
|
71
|
+
|
72
|
+
if dot_file
|
73
|
+
g.output(dot: dot_file)
|
74
|
+
end
|
75
|
+
|
76
|
+
if png_file
|
77
|
+
g.output(png: png_file)
|
78
|
+
end
|
79
|
+
|
80
|
+
if svg_file
|
81
|
+
g.output(svg: svg_file)
|
82
|
+
end
|
83
|
+
|
84
|
+
exit
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Gremlin2Dot
|
2
|
+
|
3
|
+
class Edge < Hash
|
4
|
+
def id ; self['id'] ; end
|
5
|
+
def label ; self['label'] ; end
|
6
|
+
def inVLabel ; self['inVLabel'] ; end
|
7
|
+
def outVLabel ; self['outVLabel'] ; end
|
8
|
+
def inV ; self['inV'] ; end
|
9
|
+
def outV ; self['outV'] ; end
|
10
|
+
def properties ; self['properties'] || {} ; end
|
11
|
+
def inspect ; "#<#{self.class.name} #{super}>" ; end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Vertex < Hash
|
15
|
+
def id ; self['id'] ; end
|
16
|
+
def label ; self['label'] ; end
|
17
|
+
def properties ; self['properties'] || {} ; end
|
18
|
+
def inspect ; "#<#{self.class.name} #{super}>" ; end
|
19
|
+
end
|
20
|
+
|
21
|
+
class VertexProperty < Hash
|
22
|
+
def id ; self['id'] ; end
|
23
|
+
def label ; self['label'] ; end
|
24
|
+
def value ; self['value'] ; end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
value.inspect
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
value.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def pretty_print(*args)
|
35
|
+
value.pretty_print *args
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Property < Hash
|
40
|
+
def key ; self['key'] ; end
|
41
|
+
def value ; self['value'] ; end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
value.inspect
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
value.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def pretty_print(*args)
|
52
|
+
value.pretty_print *args
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Tree
|
57
|
+
attr_accessor :key, :items
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Gremlin2Dot
|
4
|
+
|
5
|
+
class GraphBuilder
|
6
|
+
|
7
|
+
def self.build(results)
|
8
|
+
self.new().build(results)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@seen = Set.new
|
13
|
+
@g = GraphViz.new("G")
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(unmangled_data)
|
17
|
+
trees = unmangled_data['result']['data']
|
18
|
+
|
19
|
+
trees.each do |t0|
|
20
|
+
t0.each do |t1|
|
21
|
+
add_thing t1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@g
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_thing(thing)
|
29
|
+
case thing
|
30
|
+
when Vertex
|
31
|
+
node = thing
|
32
|
+
unless @seen.include? node.id
|
33
|
+
label = node.label + node.properties.entries.sort_by(&:first).map {|k, v| "\n#{k}=#{v.first}" }.join("")
|
34
|
+
@g.add_nodes(node.id, label: label)
|
35
|
+
@seen << node.id
|
36
|
+
end
|
37
|
+
when Edge
|
38
|
+
edge = thing
|
39
|
+
unless @seen.include? edge.id
|
40
|
+
label = edge.label + edge.properties.entries.sort_by(&:first).map {|k, v| "\n#{k}=#{v}" }.join("")
|
41
|
+
@g.add_edges(edge.outV, edge.inV, label: label)
|
42
|
+
@seen << edge.id
|
43
|
+
end
|
44
|
+
when Tree
|
45
|
+
add_thing thing.key
|
46
|
+
thing.items.each {|child| add_thing child}
|
47
|
+
else
|
48
|
+
$stderr.puts "Not adding #{thing.class} to the graph"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Gremlin2Dot
|
2
|
+
|
3
|
+
class Unmangler
|
4
|
+
|
5
|
+
def unmangle(d)
|
6
|
+
if d.kind_of?(Hash) and d.has_key?('@type') and d.has_key?('@value') and d.keys.count == 2
|
7
|
+
case d['@type']
|
8
|
+
when 'g:Map'
|
9
|
+
h = Hash.new
|
10
|
+
raise unless d['@value'].empty?
|
11
|
+
h
|
12
|
+
when 'g:List'
|
13
|
+
unmangle d['@value']
|
14
|
+
when 'g:Tree'
|
15
|
+
# array of hashes, where each hash has 'key'=Vertex/Edge, 'value' = ...
|
16
|
+
# Array of Hash with 'key' and 'value', where ...
|
17
|
+
# – key is a Vertex
|
18
|
+
# – value is a Hash of @type/@value
|
19
|
+
|
20
|
+
d['@value'].map do |item|
|
21
|
+
t = Tree.new
|
22
|
+
t.key = unmangle item['key']
|
23
|
+
t.items = unmangle item['value']
|
24
|
+
t
|
25
|
+
end
|
26
|
+
when 'g:Edge'
|
27
|
+
unmangle_object(d, Edge)
|
28
|
+
when 'g:Vertex'
|
29
|
+
unmangle_object(d, Vertex)
|
30
|
+
when 'g:VertexProperty'
|
31
|
+
unmangle_object(d, VertexProperty)
|
32
|
+
# discard the id
|
33
|
+
# unmangle d['@value']['value']
|
34
|
+
when 'g:Property'
|
35
|
+
unmangle_object(d, Property)
|
36
|
+
# discard the key
|
37
|
+
# unmangle d['@value']['value']
|
38
|
+
when 'g:Int32'
|
39
|
+
d['@value']
|
40
|
+
when 'g:Double'
|
41
|
+
d['@value']
|
42
|
+
when 'g:Date'
|
43
|
+
Time.at(d['@value'] / 1000.0)
|
44
|
+
else
|
45
|
+
$stderr.puts 'Ignoring unknown type ' + d['@type'] + " with value #{d['@value'].inspect[0..200]}..."
|
46
|
+
unmangle d['@value']
|
47
|
+
end
|
48
|
+
elsif d.kind_of? Hash
|
49
|
+
d.entries.map {|k, v| [k, unmangle(v)]}.to_h
|
50
|
+
elsif d.kind_of? Array
|
51
|
+
d.map {|v| unmangle v}
|
52
|
+
else
|
53
|
+
d
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def unmangle_object(d, klass)
|
58
|
+
o = klass.new
|
59
|
+
d['@value'].entries.each do |k, v|
|
60
|
+
o[k] = unmangle v
|
61
|
+
end
|
62
|
+
o
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/gremlin2dot.rb
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gremlin2dot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rachel Evans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rest-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-graphviz
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.5'
|
55
|
+
description: "\n"
|
56
|
+
email:
|
57
|
+
- rachel@rachelevans.org
|
58
|
+
executables:
|
59
|
+
- gremlin2dot
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- README.md
|
66
|
+
- bin/gremlin2dot
|
67
|
+
- lib/gremlin2dot.rb
|
68
|
+
- lib/gremlin2dot/data_model.rb
|
69
|
+
- lib/gremlin2dot/graph_builder.rb
|
70
|
+
- lib/gremlin2dot/unmangler.rb
|
71
|
+
homepage: http://rve.org.uk/gems/gremlin2dot
|
72
|
+
licenses:
|
73
|
+
- Apache-2.0
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.7.6
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Run a gremlin query, and visualise using GraphViz
|
95
|
+
test_files: []
|