dothtml 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e761c595a94b3bbafdbb7b7796ecc2b1f8a6690
4
+ data.tar.gz: 2aa1330586afdd6bd045b35c29255ee70d0d3ddc
5
+ SHA512:
6
+ metadata.gz: 27d82f94a44ca6052014e949f6bd3dfaf3d3a2958c03ff71016a6ea9418b7f6ec27d84ccc5f2d888036651a461f2e73fe17e6a3380383b0599a2b2487e604df1
7
+ data.tar.gz: dee8c3c7154326da5120f175e6e4be26a3c1821ae35f922aff673bb1ee6614bd1764a9a455c40b61c25526c606fc402a5f58df8a1c49fc474f7e0dcf807abef3
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ before_install:
3
+ - sudo apt-get install graphviz
4
+ rvm:
5
+ - 2.1.5
data/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Change Log
2
+
3
+ ## 0.0.3 - 2014-12-23
4
+ ### Added
5
+ - Rule to generate an svg file
6
+
7
+ ## 0.0.2 - 2014-12-10
8
+ ### Added
9
+ - This CHANGELOG
10
+ - Added behavior and style files. (no longer in template file)
11
+
12
+ ### Removed
13
+ - Removed temporary svg file and associated Rakefile rule.
14
+ - Removed documentation and radioboxes when not necessary.
15
+
16
+ ## 0.0.1 - 2014-12-08
17
+ ### Added
18
+ - Rakefile task rule to convert dot files to svg files
19
+ - Rakefile rule to convert svg files to html files
20
+ - Guardfile to auto generate site
21
+
22
+ ### Remove
23
+ - Removed embedding of svg images. Not ready for prime time yet.
24
+ - Removed requirement on liquid gem. Default template now in erb.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dothtml.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Keenan Brock
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Dothtml
2
+
3
+ Graphviz is a great visualization tool for coding digraphs.
4
+
5
+ d3 is a great tool for dynamic html presentations.
6
+
7
+ This tool:
8
+
9
+ - converts graphviz dot files into html.
10
+ - provides sample guard file
11
+ - Rake tasks
12
+ - hacks class attributes to embed into html file
13
+ - embeds style into html file
14
+
15
+ TODO:
16
+ - add radio button for class association
17
+ - embed external svg images into html file
18
+
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'dothtml'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install dothtml
35
+
36
+ ## Usage
37
+
38
+ TODO: Write usage instructions here
39
+
40
+ ## Similar Projects
41
+
42
+ - https://github.com/ioquatix/graphviz
43
+ - https://github.com/kui/octopress-graphviz
44
+ - https://github.com/glejeune/Ruby-Graphviz
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( https://github.com/[my-github-username]/dothtml/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ end
7
+
8
+ task :default => :test
9
+
data/bin/dothtml ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dothtml'
data/dothtml.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dothtml/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dothtml"
8
+ spec.version = Dothtml::VERSION
9
+ spec.authors = ["Keenan Brock"]
10
+ spec.email = ["keenan@thebrocks.net"]
11
+ spec.summary = %q{Make conversion of dot to html easier}
12
+ spec.description = %q{Make conversion of dot to html easier}
13
+ spec.homepage = "http://github.com/kbrock/dothtml"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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_dependency 'guard'
22
+ spec.add_dependency 'guard-rake'
23
+ spec.add_dependency 'nokogiri'
24
+ # possibly remove this
25
+ spec.add_dependency 'tilt'
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.7"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "minitest"
30
+ end
@@ -0,0 +1,126 @@
1
+ require "tilt"
2
+ require 'nokogiri'
3
+ require 'set'
4
+ require 'open3'
5
+
6
+ class DotHelper
7
+ def initialize(svg_contents)
8
+ @svg_contents = svg_contents
9
+ end
10
+
11
+ def parse_dom(contents)
12
+ Nokogiri::XML.parse(contents)
13
+ end
14
+
15
+ def dom
16
+ @dom ||= parse_dom(@svg_contents)
17
+ end
18
+
19
+ def extractChoices
20
+ end
21
+
22
+ def descriptions?
23
+ #dom.css("")
24
+ end
25
+
26
+ def extractTitle
27
+ dom.css("title").first.content()
28
+ end
29
+
30
+ # this currently has too many limitations
31
+ # working on making this more friendly
32
+ # assume unique list of filenames
33
+ def images
34
+ embedded_images = Set.new
35
+
36
+ defs = dom.create_element("def")
37
+
38
+ # assuming the images are the correct size, declare their size
39
+ dom.css("image").each do |img|
40
+ file_name = img.attributes["href"].value
41
+ id = file_name.split(".").first.split("/").last
42
+ if file_name =~ /\.svg$/ && ! embedded_images.include?(file_name)
43
+ src = parse_dom(File.read(file_name)).at("svg")
44
+ g = dom.create_element("g", id: id,
45
+ width: src["width"], height: src["height"])
46
+ defs.add_child(g)
47
+ src.children.each do |child|
48
+ g.add_child(child.clone)
49
+ end
50
+ embedded_images << file_name
51
+ end
52
+
53
+ img.name="use"
54
+ img.attributes["href"].value="##{id}"
55
+ #img.attributes["width"].remove
56
+ #img.attributes["height"].remove
57
+ #img.attributes["preserveAspectRatio"].remove
58
+ end
59
+ defs
60
+ end
61
+
62
+ def extract_id_class(old_id)
63
+ if old_id =~ /(.*?) ?class=["']?(.*?)['"]?$/
64
+ [$1, $2]
65
+ else
66
+ [old_id]
67
+ end
68
+ end
69
+
70
+ def merge_id_class(old_id, old_class)
71
+ new_id, new_class = extract_id_class(old_id)
72
+ [new_id, [old_class, new_class].compact.join(" ")]
73
+ end
74
+
75
+ # some nodes are of the form <div id="x1 class='other'" class="c1">
76
+ # assume (class= is present)
77
+ def fix_node_id(node)
78
+ new_id, new_class = merge_id_class(node["id"], node["class"])
79
+ node["id"] = new_id
80
+ node["class"] = new_class
81
+ node
82
+ end
83
+
84
+ def embed_images
85
+ dom.at("svg").children.before(images)
86
+ self
87
+ end
88
+
89
+ def fix_ids
90
+ dom.xpath("//*[contains(@id,'class=')]").each { |n| fix_node_id(n) }
91
+ self
92
+ end
93
+
94
+
95
+ # uses a fragment to remove extra xml declarations
96
+ def to_xml
97
+ dom.at("svg").to_xml
98
+ end
99
+
100
+ def write(file_name, template_name, locals)
101
+ File.write(file_name, Tilt.new(template_name).render(binding, locals))
102
+ end
103
+
104
+ def self.from_dotfile(filename)
105
+ new(svg_from_dot(File.read(filename)))
106
+ end
107
+
108
+ def self.from_dot(contents)
109
+ new(svg_from_dot(contents))
110
+ end
111
+
112
+ def self.svg_from_dot(contents)
113
+ Open3.popen3('dot -Tsvg') do |stdin, stdout, stderr|
114
+ stdout.binmode
115
+ stdin.print contents
116
+ stdin.close
117
+
118
+ err = stderr.read
119
+ if !err.nil? && !err.strip.empty?
120
+ raise "Error from graphviz:\n#{err}"
121
+ end
122
+
123
+ stdout.read.tap { |str| str.force_encoding 'UTF-8' }
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,78 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require_relative 'dot_helper'
4
+
5
+ module Dothtml
6
+ class DotTask < Rake::TaskLib
7
+ attr_accessor :template
8
+ attr_accessor :style
9
+ attr_accessor :behavior
10
+ attr_accessor :cdn
11
+ attr_accessor :d3js
12
+
13
+ attr_accessor :dot_folder
14
+ attr_accessor :html_folder
15
+
16
+ def initialize(name = :dot)
17
+ templates = File.expand_path(File.join(File.dirname(__FILE__), "..", "..","templates"))
18
+ @name = name
19
+ @style = File.join(templates, 'style.css')
20
+ @behavior = File.join(templates, 'behavior.js')
21
+ @template = File.join(templates, 'index.html.erb')
22
+ self.cdn = true
23
+ yield self if block_given?
24
+ define
25
+ end
26
+
27
+ def cdn=(val)
28
+ @d3js = val ? "//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js" : "d3.v3.js"
29
+ end
30
+
31
+ def define
32
+ desc "convert dot file into an html file"
33
+ task :dot_html, [:src, :target] do |t, params|
34
+ source = params[:src]
35
+ target = params[:target]
36
+
37
+ puts "#{source} -> #{target}"
38
+
39
+ doc = DotHelper.from_dotfile(source).fix_ids#.embed_images
40
+ doc.write target, @template,
41
+ title: doc.extractTitle,
42
+ body: doc.to_xml,
43
+ choices: doc.extractChoices,
44
+ descriptions: doc.descriptions?,
45
+ style: File.read(style),
46
+ behavior: File.read(behavior),
47
+ d3js: d3js
48
+ end
49
+
50
+ desc "convert dot file into an svg file"
51
+ task :dot_svg, [:src, :target] do |t, params|
52
+ source = params[:src]
53
+ target = params[:target]
54
+
55
+ puts "#{source} -> #{target}"
56
+
57
+ doc = DotHelper.from_dotfile(source).fix_ids#.embed_images
58
+ File.write(target, doc.to_xml)
59
+ end
60
+
61
+ rule '.html' => [".dot", style, template, behavior] do |t|
62
+ Rake::Task["dot_html"].execute(:target => t.name, :src => t.source)
63
+ Rake::Task["refresh_browser"].invoke
64
+ end
65
+
66
+ rule '.svg' => [".dot"] do |t|
67
+ Rake::Task["dot_svg"].execute(:target => t.name, :src => t.source)
68
+ end
69
+
70
+ #TODO find proper tab, offer non chrome options
71
+ desc "use applescript to refresh front most window in chrome"
72
+ task :refresh_browser do
73
+ puts "refreshing browser"
74
+ `osascript -e 'tell application "Google Chrome" to tell the active tab of its first window to reload'`
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ module Dothtml
2
+ VERSION = "0.0.3"
3
+ end
data/lib/dothtml.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "dothtml/version"
2
+
3
+ module Dothtml
4
+ # Your code goes here...
5
+ end
data/templates/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group "diagram" do
4
+ gem 'dothtml'
5
+ end
@@ -0,0 +1,6 @@
1
+ guard 'rake', :task => :html do
2
+ watch(/.dot$/)
3
+ #watch('index.html.erb')
4
+ #watch('style.css')
5
+ #watch('behavior.js')
6
+ end
@@ -0,0 +1,23 @@
1
+
2
+ # Rakefile to build dot files
3
+ # dependencies:
4
+ # mac
5
+ # graphviz to convert dot files to svg
6
+
7
+ require "dothtml/dot_task"
8
+
9
+ Dothtml::DotTask.new do |t|
10
+ #t.cdn = true
11
+ #t.template = 'index.html.liquid'
12
+ #t.style = 'style.css'
13
+ #t.behavior = 'behavior.js'
14
+ end
15
+
16
+ task :default => :html
17
+
18
+ #task :html => %w(file1.html file2.html)
19
+ task :html do
20
+ Dir.glob("*.dot").each do |f|
21
+ Rake::Task[f.sub(/\.dot$/, '.html')].invoke
22
+ end
23
+ end
@@ -0,0 +1,86 @@
1
+ var current_edge = {};
2
+
3
+ function markClass(id, className, enabled) {
4
+ if (id && className) {
5
+ d3.select("#"+id).classed(className, enabled);
6
+ }
7
+ }
8
+
9
+ function displayRelations(datum, nodeClass, enabled) {
10
+ if (!datum)
11
+ return;
12
+ // highlight current node (datum.id)
13
+ markClass(datum.id, nodeClass, enabled);
14
+ // highlight related nodes
15
+ for(var n in datum) {
16
+ if(n != "desc" && n != "id") {
17
+ markClass(datum[n], n, enabled);
18
+ }
19
+ }
20
+ // update node description
21
+ // since there are node and edge descriptions, have multiple #desc divs
22
+ var desc = enabled ? (datum.desc || "") : "";
23
+ d3.select("#desc ."+nodeClass).html(desc);
24
+ }
25
+
26
+ // store attributes in dot's tooltip attribute
27
+ // data associated with each node will split those up
28
+ function extractDatum(node, datum) {
29
+ var info = d3.select(node).select("a");
30
+ if (info && info[0] && info[0][0]) {
31
+ info = info.attr("xlink:title");
32
+ } else {
33
+ info = null;
34
+ }
35
+ if (info) {
36
+ var attrs = info.split("|");
37
+ // class:id:filename - class = (config|client|server|client_config|server_config)
38
+ for(var attr_num = 0; attr_num < attrs.length ; attr_num++) {
39
+ var attr = attrs[attr_num].trim();
40
+ attr_parts = attr.split(":");
41
+ if (attr_parts[1]) {
42
+ // TODO: handle duplicate attributes?
43
+ datum[attr_parts[0]] = attr_parts[1];
44
+ // TODO: add specific support for 3rd parameter (tablename/filename)
45
+ // if (attr_parts[2])
46
+ // attr["filename"] = attr_parts[2];
47
+ } else {
48
+ datum["desc"] = attr_parts[0];
49
+ }
50
+ }
51
+ }
52
+ return datum;
53
+ }
54
+
55
+ /* work around dot not supporting attribute `class="x"` */
56
+ function hackClass(node, datum) {
57
+ if (datum["class"]) {
58
+ d3.select(node).classed(datum["class"], true);
59
+ delete datum["class"];
60
+ }
61
+ return datum;
62
+ }
63
+
64
+ d3.selectAll(".node").datum(function() {
65
+ return hackClass(this, extractDatum(this, {"id" : this.id}));
66
+ })
67
+ .on('mouseover', function(datum) {
68
+ displayRelations(datum, 'active', true);
69
+ })
70
+ .on('mouseout', function(datum) {
71
+ displayRelations(datum, 'active', false);
72
+ });
73
+
74
+ d3.selectAll(".edge").datum(function(){
75
+ return extractDatum(this, {"id" : this.id});
76
+ })
77
+ /* selecting an edge is difficult
78
+ * instead of relying upon mouseover/mouseout, making mouseover sticky
79
+ */
80
+ .on('mouseover', function(datum) {
81
+ if (datum.id != current_edge.id) {
82
+ displayRelations(current_edge, 'src-dest', false);
83
+ current_edge = datum;
84
+ displayRelations(datum, 'src-dest', true);
85
+ }
86
+ });