graphdown 0.0.1 → 0.1.0
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 +4 -4
- data/.travis.yml +6 -0
- data/README.md +38 -24
- data/bin/graphdown +4 -0
- data/examples/path_to_graphdown.html +31 -0
- data/examples/path_to_graphdown.md +5 -0
- data/examples/path_to_graphdown.png +0 -0
- data/examples/servers_arrangement.html +39 -0
- data/examples/servers_arrangement.md +3 -0
- data/examples/servers_arrangement.png +0 -0
- data/graphdown.gemspec +5 -1
- data/lib/graphdown/cli.rb +47 -0
- data/lib/graphdown/edge.rb +118 -0
- data/lib/graphdown/graph.rb +104 -0
- data/lib/graphdown/node.rb +76 -0
- data/lib/graphdown/parser.rb +43 -0
- data/lib/graphdown/point.rb +10 -0
- data/lib/graphdown/renderable.rb +8 -30
- data/lib/graphdown/version.rb +3 -0
- data/lib/graphdown.rb +7 -4
- data/spec/fixtures/sample.md +1 -18
- data/spec/fixtures/sample.svg +18 -0
- data/spec/fixtures/sample_without_graph.md +2 -6
- data/spec/graphdown/graph_spec.rb +32 -0
- data/spec/graphdown/node_spec.rb +35 -0
- data/spec/graphdown/parser_spec.rb +33 -0
- data/spec/graphdown/renderable_spec.rb +14 -26
- data/spec/spec_helper.rb +1 -0
- metadata +67 -19
- data/examples/parser.rb +0 -13
- data/examples/sample.html +0 -8
- data/examples/sample.md +0 -18
- data/examples/sample.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25f4809d61a0512f86a702e492528795fbc3951d
|
4
|
+
data.tar.gz: 89940ed3e506736155d2a841d8e4f5abbcd38682
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d67dab59ae625d6dab02cc780f59b2326d3772d594daf24640c58ca8f2cd8b75a149a56da4167ae5c4709ed2620a29f46607785667a6aad53d3b015d1dd0a278
|
7
|
+
data.tar.gz: b5bfa36722e6e9cd9e3fd907bf76c7aebfb8481fbd9d669bda6c8ad6a327729f15169861bcb8ca0161a46430e4bd8c4230fb3bad5aa372b9977e434e1addd42d
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -8,11 +8,17 @@ Markdown extension for embedding graphs.
|
|
8
8
|
$ gem install graphdown
|
9
9
|
```
|
10
10
|
|
11
|
-
##
|
11
|
+
## Usage
|
12
12
|
|
13
|
-
|
13
|
+
### CLI
|
14
14
|
|
15
|
-
|
15
|
+
```sh
|
16
|
+
$ graphdown sample.md
|
17
|
+
```
|
18
|
+
|
19
|
+
This command generates `sample.html`, which is parsed into HTML with graphs in format of SVG.
|
20
|
+
|
21
|
+
### Redcarpet
|
16
22
|
|
17
23
|
```rb
|
18
24
|
require "redcarpet"
|
@@ -20,34 +26,42 @@ require "graphdown"
|
|
20
26
|
|
21
27
|
class BaseRenderer < Redcarpet::Render::HTML
|
22
28
|
include Graphdown::Renderable
|
23
|
-
# include other extensions
|
24
29
|
end
|
25
30
|
|
26
31
|
markdown = Redcarpet::Markdown.new(BaseRenderer, fenced_code_blocks: true)
|
27
|
-
markdown.render(content)
|
28
32
|
```
|
29
33
|
|
30
|
-
##
|
34
|
+
## Graph notation
|
31
35
|
|
32
|
-
|
33
|
-
# Views transition
|
36
|
+
Graphdown extends following notations for graphs.
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
C [label = "new.html"];
|
38
|
+
- `[label name]`: Node named "label name"
|
39
|
+
- `[label A], [label B], ...`: Multiple nodes
|
40
|
+
- `->`: Unidirectional edge
|
41
|
+
- `<->`: Bidirectional edge
|
40
42
|
|
41
|
-
|
42
|
-
A -> C;
|
43
|
-
C -> A [label = "redirect"];
|
44
|
-
}
|
45
|
-
\```
|
43
|
+
Graphdown parses these notations into graph images in SVG format.
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
### Examples
|
46
|
+
|
47
|
+
<pre>
|
48
|
+
# Servers arrangement
|
49
|
+
|
50
|
+
[Load balancer] -> [Web server 1], [Web server 2], [Web server 3] -> [DB server], [Cache server]
|
51
|
+
</pre>
|
52
|
+
|
53
|
+

|
54
|
+
|
55
|
+
<pre>
|
56
|
+
# Path to naoty/graphdown on GitHub
|
57
|
+
|
58
|
+
[/] -> [/naoty] -> [/naoty/graphdown]
|
59
|
+
[/] -> [/search] -> [/naoty/graphdown]
|
60
|
+
[/] -> [/notifications] -> [/naoty/graphdown]
|
61
|
+
</pre>
|
62
|
+
|
63
|
+

|
64
|
+
|
65
|
+
## References
|
52
66
|
|
53
|
-
|
67
|
+
- Kazuo Misue, Kouzou Sugiyama, On Automatic Drowing of Compound Graphs for Computer Aided Diagrammatical Thinking, Journal of Information Processing Society of Japan, 1989, Vol.30, No.10, p1324-1334.
|
data/bin/graphdown
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
<h1>Path to naoty/graphdown on GitHub</h1>
|
2
|
+
<svg width="395" height="275" xmlns="http://www.w3.org/2000/svg">
|
3
|
+
<g fill="none" stroke="black">
|
4
|
+
<rect x="10" y="10" width="33" height="35"/>
|
5
|
+
<rect x="10" y="95" width="78" height="35"/>
|
6
|
+
<rect x="10" y="180" width="168" height="35"/>
|
7
|
+
<rect x="108" y="95" width="87" height="35"/>
|
8
|
+
<rect x="215" y="95" width="150" height="35"/>
|
9
|
+
</g>
|
10
|
+
<g font-size="18" font-family="monospace" text-anchor="middle">
|
11
|
+
<text x="26" y="37">/</text>
|
12
|
+
<text x="49" y="122">/naoty</text>
|
13
|
+
<text x="94" y="207">/naoty/graphdown</text>
|
14
|
+
<text x="151" y="122">/search</text>
|
15
|
+
<text x="290" y="122">/notifications</text>
|
16
|
+
</g>
|
17
|
+
<g stroke="black" stroke-width="2">
|
18
|
+
<path d="M 18.25 45 L 49.0 95" fill="none"/>
|
19
|
+
<path d="M 49.0 95 L 40.204218667115356 90.24245538706381 L 48.72226382799822 85.00385761312086 Z" fill="black"/>
|
20
|
+
<path d="M 26.5 45 L 151.5 95" fill="none"/>
|
21
|
+
<path d="M 151.5 95 L 141.602202606946 96.42604584991292 L 145.31610937048706 87.14127894106032 Z" fill="black"/>
|
22
|
+
<path d="M 34.75 45 L 290.0 95" fill="none"/>
|
23
|
+
<path d="M 290.0 95 L 280.54010074624506 98.24196022628374 L 282.46243045910205 88.42846704214861 Z" fill="black"/>
|
24
|
+
<path d="M 49.0 130 L 52.0 180" fill="none"/>
|
25
|
+
<path d="M 52.0 180 L 46.49029332005865 171.6547539100989 L 56.47234177471645 171.05583100281942 Z" fill="black"/>
|
26
|
+
<path d="M 151.5 130 L 94.0 180" fill="none"/>
|
27
|
+
<path d="M 94.0 180 L 97.25418194567223 170.54429802370757 L 103.81596909492009 178.0903532453426 Z" fill="black"/>
|
28
|
+
<path d="M 290.0 130 L 136.0 180" fill="none"/>
|
29
|
+
<path d="M 136.0 180 L 142.69294863675958 172.57003105351723 L 145.78101617536336 182.08127907241686 Z" fill="black"/>
|
30
|
+
</g>
|
31
|
+
</svg>
|
Binary file
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<h1>Servers arrangement</h1>
|
2
|
+
<svg width="476" height="275" xmlns="http://www.w3.org/2000/svg">
|
3
|
+
<g fill="none" stroke="black">
|
4
|
+
<rect x="10" y="10" width="141" height="35"/>
|
5
|
+
<rect x="10" y="95" width="132" height="35"/>
|
6
|
+
<rect x="162" y="95" width="132" height="35"/>
|
7
|
+
<rect x="314" y="95" width="132" height="35"/>
|
8
|
+
<rect x="10" y="180" width="105" height="35"/>
|
9
|
+
<rect x="135" y="180" width="132" height="35"/>
|
10
|
+
</g>
|
11
|
+
<g font-size="18" font-family="monospace" text-anchor="middle">
|
12
|
+
<text x="80" y="37">Load balancer</text>
|
13
|
+
<text x="76" y="122">Web server 1</text>
|
14
|
+
<text x="228" y="122">Web server 2</text>
|
15
|
+
<text x="380" y="122">Web server 3</text>
|
16
|
+
<text x="62" y="207">DB server</text>
|
17
|
+
<text x="201" y="207">Cache server</text>
|
18
|
+
</g>
|
19
|
+
<g stroke="black" stroke-width="2">
|
20
|
+
<path d="M 45.25 45 L 76.0 95" fill="none"/>
|
21
|
+
<path d="M 76.0 95 L 67.20421866711536 90.24245538706381 L 75.72226382799822 85.00385761312086 Z" fill="black"/>
|
22
|
+
<path d="M 80.5 45 L 228.0 95" fill="none"/>
|
23
|
+
<path d="M 228.0 95 L 218.1929724878952 96.95504766612466 L 221.4033652994742 87.48438887196666 Z" fill="black"/>
|
24
|
+
<path d="M 115.75 45 L 380.0 95" fill="none"/>
|
25
|
+
<path d="M 380.0 95 L 370.56115218759226 98.30274915399355 L 372.4203114241102 88.4770925889965 Z" fill="black"/>
|
26
|
+
<path d="M 54.0 130 L 36.25 180" fill="none"/>
|
27
|
+
<path d="M 36.25 180 L 34.435344142727494 170.16602704296648 L 43.85914247228395 173.51147544995902 Z" fill="black"/>
|
28
|
+
<path d="M 98.0 130 L 168.0 180" fill="none"/>
|
29
|
+
<path d="M 168.0 180 L 158.04667045165723 179.03499694192848 L 163.85905238884823 170.8976622298611 Z" fill="black"/>
|
30
|
+
<path d="M 206.0 130 L 62.5 180" fill="none"/>
|
31
|
+
<path d="M 62.5 180 L 69.0328881004204 172.42891202881742 L 72.3231985675411 181.87210306945383 Z" fill="black"/>
|
32
|
+
<path d="M 250.0 130 L 201.0 180" fill="none"/>
|
33
|
+
<path d="M 201.0 180 L 203.4904951358704 170.3150924641375 L 210.63262352729546 177.3143782877341 Z" fill="black"/>
|
34
|
+
<path d="M 358.0 130 L 88.75 180" fill="none"/>
|
35
|
+
<path d="M 88.75 180 L 96.3517865603404 173.50285900637604 L 98.17758243261781 183.33476977858993 Z" fill="black"/>
|
36
|
+
<path d="M 402.0 130 L 234.0 180" fill="none"/>
|
37
|
+
<path d="M 234.0 180 L 240.87417050853045 172.7373710118408 L 243.72670645627238 182.3218917962536 Z" fill="black"/>
|
38
|
+
</g>
|
39
|
+
</svg>
|
Binary file
|
data/graphdown.gemspec
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
lib = File.expand_path("../lib", __FILE__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
|
4
|
+
require "graphdown/version"
|
5
|
+
|
4
6
|
Gem::Specification.new do |spec|
|
5
7
|
spec.name = "graphdown"
|
6
|
-
spec.version =
|
8
|
+
spec.version = Graphdown::VERSION
|
7
9
|
spec.authors = ["Naoto Kaneko"]
|
8
10
|
spec.email = ["naoty.k@gmail.com"]
|
9
11
|
spec.summary = %q{Markdown extension for embedding graphs.}
|
@@ -15,6 +17,8 @@ Gem::Specification.new do |spec|
|
|
15
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
18
|
spec.require_paths = ["lib"]
|
17
19
|
|
20
|
+
spec.add_dependency "svgen"
|
21
|
+
spec.add_dependency "thor", "~> 0.18.1"
|
18
22
|
spec.add_dependency "redcarpet", "~> 3.0.0"
|
19
23
|
spec.add_development_dependency "bundler", "~> 1.5"
|
20
24
|
spec.add_development_dependency "rake"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "redcarpet"
|
2
|
+
require "pathname"
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module Graphdown
|
6
|
+
class CLI
|
7
|
+
def self.start(args)
|
8
|
+
method_name = args.shift
|
9
|
+
new.send(method_name, *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@options = {}
|
14
|
+
@option_parser = OptionParser.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse(name, *args)
|
18
|
+
@option_parser.on("-o", "--output FILENAME") { |filename| @options[:output] = filename }
|
19
|
+
@option_parser.parse!(args)
|
20
|
+
|
21
|
+
markdown_path = Pathname.new(name).expand_path
|
22
|
+
output_filename = @options[:output] || File.basename(name, ".md") + ".html"
|
23
|
+
html_path = markdown_path.dirname.join(output_filename)
|
24
|
+
|
25
|
+
renderer = Class.new(Redcarpet::Render::HTML)
|
26
|
+
renderer.send(:include, Graphdown::Renderable)
|
27
|
+
markdown = Redcarpet::Markdown.new(renderer)
|
28
|
+
|
29
|
+
html = markdown.render(markdown_path.read)
|
30
|
+
html_path.open("wb") { |file| file.write(html) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(name, *args)
|
34
|
+
case name.to_s
|
35
|
+
when /\.md$/
|
36
|
+
parse(name.to_s, *args)
|
37
|
+
when "-h", "--help"
|
38
|
+
args << "--help"
|
39
|
+
parse(name.to_s, *args)
|
40
|
+
when "-v", "--version"
|
41
|
+
puts Graphdown::VERSION
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "matrix"
|
2
|
+
|
3
|
+
module Graphdown
|
4
|
+
class Edge
|
5
|
+
attr_accessor :origin, :target
|
6
|
+
attr_reader :direction
|
7
|
+
|
8
|
+
DIRECTION = [:forward, :backward, :two_way].freeze
|
9
|
+
ARROW_SIDE_LENGTH = 10
|
10
|
+
|
11
|
+
def initialize(origin_node, target_node, direction = :forward)
|
12
|
+
@origin_node = origin_node
|
13
|
+
@target_node = target_node
|
14
|
+
@direction = DIRECTION.include?(direction) ? direction : :forward
|
15
|
+
@origin = Point.new
|
16
|
+
@target = Point.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def forward?
|
20
|
+
@direction == :forward || @direction == :two_way
|
21
|
+
end
|
22
|
+
|
23
|
+
def backward?
|
24
|
+
@direction == :backward || @direction == :two_way
|
25
|
+
end
|
26
|
+
|
27
|
+
def parent
|
28
|
+
case @direction
|
29
|
+
when :forward then @origin_node
|
30
|
+
when :backward then @target_node
|
31
|
+
when :two_way then @origin_node
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def child
|
36
|
+
case @direction
|
37
|
+
when :forward then @target_node
|
38
|
+
when :backward then @origin_node
|
39
|
+
when :two_way then @target_node
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def line_d
|
44
|
+
"M #{@origin.x} #{@origin.y} L #{@target.x} #{@target.y}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def arrow_d
|
48
|
+
ratio = ARROW_SIDE_LENGTH / length
|
49
|
+
|
50
|
+
v1 = Graphdown::Edge::Vector.new(@target.x - @origin.x, @target.y - @origin.y)
|
51
|
+
p1 = v1.scale(ratio).rotate(Math::PI / 6 * 5).point
|
52
|
+
|
53
|
+
v2 = Graphdown::Edge::Vector.new(@target.x - @origin.x, @target.y - @origin.y)
|
54
|
+
p2 = v2.scale(ratio).rotate(-Math::PI / 6 * 5).point
|
55
|
+
|
56
|
+
p1.x += @target.x
|
57
|
+
p1.y += @target.y
|
58
|
+
p2.x += @target.x
|
59
|
+
p2.y += @target.y
|
60
|
+
|
61
|
+
"M #{@target.x} #{@target.y} L #{p1.x} #{p1.y} L #{p2.x} #{p2.y} Z"
|
62
|
+
end
|
63
|
+
|
64
|
+
def reverse_arrow_d
|
65
|
+
ratio = ARROW_SIDE_LENGTH / length
|
66
|
+
|
67
|
+
v1 = Graphdown::Edge::Vector.new(@origin.x - @target.x, @origin.y - @target.y)
|
68
|
+
p1 = v1.scale(ratio).rotate(Math::PI / 6 * 5).point
|
69
|
+
|
70
|
+
v2 = Graphdown::Edge::Vector.new(@origin.x - @target.x, @origin.y - @target.y)
|
71
|
+
p2 = v2.scale(ratio).rotate(-Math::PI / 6 * 5).point
|
72
|
+
|
73
|
+
p1.x += @origin.x
|
74
|
+
p1.y += @origin.y
|
75
|
+
p2.x += @origin.x
|
76
|
+
p2.y += @origin.y
|
77
|
+
|
78
|
+
"M #{@origin.x} #{@origin.y} L #{p1.x} #{p1.y} L #{p2.x} #{p2.y} Z"
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def length
|
84
|
+
dx = @target.x - @origin.x
|
85
|
+
dy = @target.y - @origin.y
|
86
|
+
Math.sqrt(dx ** 2 + dy ** 2)
|
87
|
+
end
|
88
|
+
|
89
|
+
class Vector
|
90
|
+
def initialize(x, y)
|
91
|
+
@v = Matrix[[x, y]]
|
92
|
+
@matrices = []
|
93
|
+
end
|
94
|
+
|
95
|
+
def point
|
96
|
+
@matrices.each { |matrix| @v *= matrix }
|
97
|
+
x, y = @v.row(0).to_a
|
98
|
+
Point.new(x, y)
|
99
|
+
end
|
100
|
+
|
101
|
+
def scale(ratio)
|
102
|
+
unit_vector = @v.row(0).normalize
|
103
|
+
nx, ny = unit_vector.map(&:to_f).to_a
|
104
|
+
scaling_matrix = Matrix[[1 + (ratio - 1) * nx ** 2, (ratio - 1) * nx * ny],
|
105
|
+
[(ratio - 1) * nx * ny, 1 + (ratio - 1) * ny ** 2]]
|
106
|
+
@matrices << scaling_matrix
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def rotate(radian)
|
111
|
+
rotation_matrix = Matrix[[Math.cos(radian), Math.sin(radian)],
|
112
|
+
[-Math.sin(radian), Math.cos(radian)]]
|
113
|
+
@matrices << rotation_matrix
|
114
|
+
self
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "svgen"
|
2
|
+
|
3
|
+
module Graphdown
|
4
|
+
class Graph
|
5
|
+
attr_reader :nodes
|
6
|
+
|
7
|
+
PADDING = 10
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@nodes = []
|
11
|
+
@width = 0
|
12
|
+
@height = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(node)
|
16
|
+
labels = @nodes.map(&:label)
|
17
|
+
@nodes << node unless labels.include?(node.label)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_node_by_label(label)
|
21
|
+
(@nodes || []).select { |node| node.label == label }.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def layered_nodes
|
25
|
+
@layered_nodes ||= @nodes.sort_by(&:level).group_by(&:level).values
|
26
|
+
end
|
27
|
+
|
28
|
+
def layer_nodes
|
29
|
+
nodes_with_level = @nodes.select { |node| node.level >= 1 }
|
30
|
+
begin
|
31
|
+
diff_total = 0
|
32
|
+
nodes_with_level.each do |node|
|
33
|
+
node.children.each do |child|
|
34
|
+
if node.level >= child.level
|
35
|
+
child.level += node.level + 1
|
36
|
+
diff_total += node.level + 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end while diff_total != 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def layout_nodes
|
44
|
+
layer_widths = []
|
45
|
+
layer_offset = PADDING
|
46
|
+
layered_nodes.each do |nodes|
|
47
|
+
node_offset = PADDING
|
48
|
+
nodes.each do |node|
|
49
|
+
node.x = node_offset
|
50
|
+
node.y = layer_offset
|
51
|
+
node_offset += node.width + Node::MARGIN_RIGHT
|
52
|
+
end
|
53
|
+
layer_widths << node_offset + PADDING
|
54
|
+
layer_offset += nodes.map(&:height).max + Node::MARGIN_BOTTOM
|
55
|
+
end
|
56
|
+
@width = layer_widths.max
|
57
|
+
@height = layer_offset + PADDING
|
58
|
+
end
|
59
|
+
|
60
|
+
def layout_edges
|
61
|
+
@nodes.each do |node|
|
62
|
+
node.child_edges.each_with_index do |edge, index|
|
63
|
+
edge.origin.x = node.x + (node.width.to_f * (index + 1) / (node.child_edges.count + 1))
|
64
|
+
edge.origin.y = node.y + node.height
|
65
|
+
end
|
66
|
+
|
67
|
+
node.parent_edges.each_with_index do |edge, index|
|
68
|
+
edge.target.x = node.x + (node.width.to_f * (index + 1) / (node.parent_edges.count + 1))
|
69
|
+
edge.target.y = node.y
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_svg
|
75
|
+
svg = SVGen::SVG.new(width: @width, height: @height) do |svg|
|
76
|
+
# nodes
|
77
|
+
svg.g(fill: "none", stroke: "black") do |g|
|
78
|
+
@nodes.each do |node|
|
79
|
+
g.rect(x: node.x, y: node.y, width: node.width, height: node.height)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# texts
|
84
|
+
svg.g("font-size" => Node::FONT_SIZE, "font-family" => Node::FONT_FAMILY, "text-anchor" => "middle") do |g|
|
85
|
+
@nodes.each do |node|
|
86
|
+
g.text(node.label, x: node.x + node.width / 2, y: node.y + Node::WORD_HEIGHT + Node::PADDING_TOP / 2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# edges
|
91
|
+
svg.g(stroke: "black", "stroke-width" => 2) do |g|
|
92
|
+
@nodes.each do |node|
|
93
|
+
node.child_edges.each do |edge|
|
94
|
+
g.path(d: edge.line_d, fill: "none")
|
95
|
+
g.path(d: edge.arrow_d, fill: "black") if edge.forward?
|
96
|
+
g.path(d: edge.reverse_arrow_d, fill: "black") if edge.backward?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
svg.generate
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Graphdown
|
2
|
+
class Node
|
3
|
+
attr_accessor :x, :y, :level
|
4
|
+
attr_reader :label, :parent_edges, :child_edges
|
5
|
+
|
6
|
+
FONT_SIZE = 18
|
7
|
+
FONT_FAMILY = "monospace"
|
8
|
+
|
9
|
+
WORD_HEIGHT = 25
|
10
|
+
PADDING_TOP = 5
|
11
|
+
PADDING_LEFT = 10
|
12
|
+
MARGIN_RIGHT = 20
|
13
|
+
MARGIN_BOTTOM = 50
|
14
|
+
|
15
|
+
def initialize(label)
|
16
|
+
@x = 0
|
17
|
+
@y = 0
|
18
|
+
@level = 0
|
19
|
+
@label = label
|
20
|
+
@parent_edges = []
|
21
|
+
@child_edges = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def width
|
25
|
+
text_width = 13 + (@label.length - 1) * 9
|
26
|
+
text_width + PADDING_LEFT * 2
|
27
|
+
end
|
28
|
+
|
29
|
+
def height
|
30
|
+
WORD_HEIGHT + PADDING_TOP * 2
|
31
|
+
end
|
32
|
+
|
33
|
+
def connect(child, direction = :forward)
|
34
|
+
child.level = 1
|
35
|
+
|
36
|
+
# Prevent closed path
|
37
|
+
direction = ancestors.include?(child) ? :backward : direction
|
38
|
+
edge = Edge.new(self, child, direction)
|
39
|
+
if direction == :backward
|
40
|
+
self.parent_edges << edge
|
41
|
+
child.child_edges << edge
|
42
|
+
else
|
43
|
+
self.child_edges << edge
|
44
|
+
child.parent_edges << edge
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parents
|
49
|
+
parent_edges.map(&:parent)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ancestors
|
53
|
+
ancestors = []
|
54
|
+
ascend = ->(node) do
|
55
|
+
ancestors << node
|
56
|
+
node.parents.each { |parent| ascend.call(parent) }
|
57
|
+
end
|
58
|
+
parents.each { |parent| ascend.call(parent) }
|
59
|
+
ancestors
|
60
|
+
end
|
61
|
+
|
62
|
+
def children
|
63
|
+
child_edges.map(&:child)
|
64
|
+
end
|
65
|
+
|
66
|
+
def descendants
|
67
|
+
descendants = []
|
68
|
+
descend = ->(node) do
|
69
|
+
descendants << node
|
70
|
+
node.children.each { |child| descend.call(child) }
|
71
|
+
end
|
72
|
+
children.each { |child| descend.call(child) }
|
73
|
+
descendants
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Graphdown
|
2
|
+
class Parser
|
3
|
+
attr_reader :graph
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@graph = Graph.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(text)
|
10
|
+
tokens = text.split(/(?<=[\]>])\s+/)
|
11
|
+
2.step(tokens.size, 2) do |n|
|
12
|
+
# origins
|
13
|
+
origin_labels = tokens[n - 2].scan(/(?<=\[).+?(?=\])/)
|
14
|
+
origins = origin_labels.map do |label|
|
15
|
+
@graph.find_node_by_label(label) || Node.new(label)
|
16
|
+
end
|
17
|
+
|
18
|
+
# targets
|
19
|
+
target_labels = tokens[n].scan(/(?<=\[).+?(?=\])/)
|
20
|
+
targets = target_labels.map do |label|
|
21
|
+
@graph.find_node_by_label(label) || Node.new(label)
|
22
|
+
end
|
23
|
+
|
24
|
+
# edges
|
25
|
+
direction = (tokens[n - 1] == "<->") ? :two_way : :forward
|
26
|
+
origins.each do |origin|
|
27
|
+
@graph << origin
|
28
|
+
targets.each do |target|
|
29
|
+
origin.connect(target, direction)
|
30
|
+
@graph << target
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def output
|
37
|
+
@graph.layer_nodes
|
38
|
+
@graph.layout_nodes
|
39
|
+
@graph.layout_edges
|
40
|
+
@graph.to_svg
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/graphdown/renderable.rb
CHANGED
@@ -1,38 +1,16 @@
|
|
1
|
-
require "
|
2
|
-
require "pry"
|
1
|
+
require "cgi"
|
3
2
|
|
4
3
|
module Graphdown
|
5
4
|
module Renderable
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def block_code_with_graphdown(code, language)
|
15
|
-
if language == "dot"
|
16
|
-
if code =~ /digraph\s+(\w+)\s+{/
|
17
|
-
dot_path = Pathname.pwd.join("#{$1}.dot")
|
18
|
-
dot_path.open("wb") { |f| f.write(code) }
|
19
|
-
graph_path = Pathname.pwd.join("#{$1}.png")
|
20
|
-
generate_graph(dot_path.to_s, graph_path.to_s)
|
21
|
-
dot_path.delete
|
22
|
-
%(<img src="#{graph_path.to_s}"/>)
|
23
|
-
else
|
24
|
-
%(<img src="#"/>)
|
25
|
-
end
|
5
|
+
def paragraph(text)
|
6
|
+
if text =~ /^\s?\[.+\]\s?$/
|
7
|
+
parser = Parser.new
|
8
|
+
unescaped_text = CGI.unescape_html(text)
|
9
|
+
unescaped_text.split($/).each { |line| parser.parse(line) }
|
10
|
+
parser.output
|
26
11
|
else
|
27
|
-
|
12
|
+
%(<p>#{text}</p>)
|
28
13
|
end
|
29
14
|
end
|
30
|
-
alias block_code block_code_with_graphdown
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def generate_graph(source, output)
|
35
|
-
system %(dot -Tpng -o #{output} #{source})
|
36
|
-
end
|
37
15
|
end
|
38
16
|
end
|
data/lib/graphdown.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
|
4
|
-
require "redcarpet"
|
1
|
+
require "graphdown/version"
|
5
2
|
require "graphdown/renderable"
|
3
|
+
require "graphdown/cli"
|
4
|
+
require "graphdown/parser"
|
5
|
+
require "graphdown/graph"
|
6
|
+
require "graphdown/node"
|
7
|
+
require "graphdown/edge"
|
8
|
+
require "graphdown/point"
|
data/spec/fixtures/sample.md
CHANGED
@@ -1,18 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
```dot
|
4
|
-
digraph sample {
|
5
|
-
A [label = "index.html"];
|
6
|
-
B [label = "show.html"];
|
7
|
-
C [label = "new.html"];
|
8
|
-
|
9
|
-
A -> B [dir = both];
|
10
|
-
A -> C;
|
11
|
-
C -> A [label = "redirect"];
|
12
|
-
}
|
13
|
-
```
|
14
|
-
|
15
|
-
- Users visit show.html from index.html.
|
16
|
-
- Users visit index.html from show.html.
|
17
|
-
- Users visit new.html from index.html.
|
18
|
-
- Users are redirected to index.html from new.html.
|
1
|
+
[a] -> [b] <-> [c]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<svg width="73" height="275" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<g fill="none" stroke="black">
|
3
|
+
<rect x="10" y="10" width="33" height="35"/>
|
4
|
+
<rect x="10" y="95" width="33" height="35"/>
|
5
|
+
<rect x="10" y="180" width="33" height="35"/>
|
6
|
+
</g>
|
7
|
+
<g font-size="18" font-family="monospace" text-anchor="middle">
|
8
|
+
<text x="26" y="37">a</text>
|
9
|
+
<text x="26" y="122">b</text>
|
10
|
+
<text x="26" y="207">c</text>
|
11
|
+
</g>
|
12
|
+
<g stroke="black" stroke-width="2">
|
13
|
+
<path d="M 26.5 45 L 26.5 95" fill="none"/>
|
14
|
+
<path d="M 26.5 95 L 31.5 85 L 21.5 85 Z" fill="black"/>
|
15
|
+
<path d="M 26.5 130 L 26.5 180" fill="none"/>
|
16
|
+
<path d="M 26.5 180 L 31.5 170 L 21.5 170 Z" fill="black"/>
|
17
|
+
</g>
|
18
|
+
</svg>
|
@@ -1,7 +1,3 @@
|
|
1
|
-
#
|
1
|
+
# Sample markdown
|
2
2
|
|
3
|
-
|
4
|
-
SampleApp::Application.routes.draw do
|
5
|
-
resources :contents
|
6
|
-
end
|
7
|
-
```
|
3
|
+
Consectetur amet amet deserunt eos ducimus possimus rem nam esse eaque repudiandae? Nemo recusandae vero nesciunt maxime voluptatum accusamus laborum reiciendis soluta! Earum deleniti quaerat quia quisquam commodi dolores facere.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Graphdown::Graph do
|
4
|
+
let(:graph) { Graphdown::Graph.new }
|
5
|
+
let(:a) { Graphdown::Node.new("a") }
|
6
|
+
let(:b) { Graphdown::Node.new("b") }
|
7
|
+
let(:c) { Graphdown::Node.new("c") }
|
8
|
+
let(:d) { Graphdown::Node.new("d") }
|
9
|
+
let(:e) { Graphdown::Node.new("e") }
|
10
|
+
|
11
|
+
before do
|
12
|
+
[a, b, c, d, e].each { |node| graph << node }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#find_node_by_label" do
|
16
|
+
it "finds node" do
|
17
|
+
node = graph.find_node_by_label("a")
|
18
|
+
expect(node).to equal a
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#layered_nodes" do
|
23
|
+
it "returns layered nodes" do
|
24
|
+
e.connect(d)
|
25
|
+
e.connect(c)
|
26
|
+
d.connect(b)
|
27
|
+
b.connect(a)
|
28
|
+
graph.layer_nodes
|
29
|
+
expect(graph.layered_nodes).to match_array [[e], [d, c], [b], [a]]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Graphdown::Node do
|
4
|
+
let(:a) { Graphdown::Node.new("a") }
|
5
|
+
let(:b) { Graphdown::Node.new("b") }
|
6
|
+
let(:c) { Graphdown::Node.new("c") }
|
7
|
+
|
8
|
+
describe "#connect" do
|
9
|
+
it "connects node forward" do
|
10
|
+
a.connect(b)
|
11
|
+
expect(a.children).to be_include(b)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "connects node backward" do
|
15
|
+
a.connect(b, :backward)
|
16
|
+
expect(a.parents).to be_include(b)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "prevents closed path" do
|
20
|
+
a.connect(b)
|
21
|
+
b.connect(c)
|
22
|
+
c.connect(a)
|
23
|
+
expect(a.ancestors).to match_array []
|
24
|
+
expect(a.descendants).to match_array [b, c, c]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#ancestors" do
|
29
|
+
it "returns parents of parents" do
|
30
|
+
a.connect(b)
|
31
|
+
b.connect(c)
|
32
|
+
expect(c.ancestors).to match_array [a, b]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Graphdown::Parser do
|
4
|
+
let(:parser) { Graphdown::Parser.new }
|
5
|
+
|
6
|
+
describe "#parse" do
|
7
|
+
let(:content) { "[a] -> [b] <-> [c]" }
|
8
|
+
|
9
|
+
before do
|
10
|
+
parser.parse(content)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "parses text into nodes" do
|
14
|
+
labels = parser.graph.nodes.map(&:label)
|
15
|
+
expect(labels).to eq ["a", "b", "c"]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "parses arrows into edges" do
|
19
|
+
edges = parser.graph.nodes.map(&:child_edges).flatten
|
20
|
+
directions = edges.map(&:direction)
|
21
|
+
expect(directions).to eq [:forward, :two_way]
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when a node group is given" do
|
25
|
+
let(:content) { "[a] -> [b], [c], [d] -> [e]" }
|
26
|
+
|
27
|
+
it "parses text into nodes" do
|
28
|
+
labels = parser.graph.nodes.map(&:label)
|
29
|
+
expect(labels).to eq ["a", "b", "c", "d", "e"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,38 +1,26 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
class BaseRenderer < Redcarpet::Render::HTML
|
4
|
-
include Graphdown::Renderable
|
5
|
-
end
|
6
|
-
|
7
3
|
describe Graphdown::Renderable do
|
8
|
-
let(:markdown)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
let(:content) { fixtures_path.join("sample.md").read }
|
15
|
-
after do
|
16
|
-
graph_path.delete if graph_path.exist?
|
17
|
-
end
|
4
|
+
let(:markdown) do
|
5
|
+
renderer = Class.new(Redcarpet::Render::HTML)
|
6
|
+
renderer.send(:include, Graphdown::Renderable)
|
7
|
+
Redcarpet::Markdown.new(renderer)
|
8
|
+
end
|
9
|
+
let(:fixtures_path) { Pathname.new("./spec/fixtures") }
|
18
10
|
|
19
|
-
|
11
|
+
context "when graph text is passed" do
|
12
|
+
it "generates svg tag" do
|
13
|
+
content = fixtures_path.join("sample.md").read
|
20
14
|
html = markdown.render(content)
|
21
|
-
expect(html).to match /<
|
22
|
-
end
|
23
|
-
|
24
|
-
it "generates graph image file" do
|
25
|
-
markdown.render(content)
|
26
|
-
expect(graph_path).to be_exist
|
15
|
+
expect(html).to match /<svg.+>.+<\/svg>/m
|
27
16
|
end
|
28
17
|
end
|
29
18
|
|
30
|
-
context "when
|
31
|
-
|
32
|
-
|
33
|
-
it "generates code tag" do
|
19
|
+
context "when normal text is passed" do
|
20
|
+
it "generates p tag" do
|
21
|
+
content = fixtures_path.join("sample_without_graph.md").read
|
34
22
|
html = markdown.render(content)
|
35
|
-
expect(html).to match /<
|
23
|
+
expect(html).to match /<p>.+<\/p>/
|
36
24
|
end
|
37
25
|
end
|
38
26
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,92 +1,136 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Naoto Kaneko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: svgen
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.18.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.18.1
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: redcarpet
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
|
-
- - ~>
|
45
|
+
- - "~>"
|
18
46
|
- !ruby/object:Gem::Version
|
19
47
|
version: 3.0.0
|
20
48
|
type: :runtime
|
21
49
|
prerelease: false
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
23
51
|
requirements:
|
24
|
-
- - ~>
|
52
|
+
- - "~>"
|
25
53
|
- !ruby/object:Gem::Version
|
26
54
|
version: 3.0.0
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: bundler
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
|
-
- - ~>
|
59
|
+
- - "~>"
|
32
60
|
- !ruby/object:Gem::Version
|
33
61
|
version: '1.5'
|
34
62
|
type: :development
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
|
-
- - ~>
|
66
|
+
- - "~>"
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '1.5'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rake
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
|
-
- -
|
73
|
+
- - ">="
|
46
74
|
- !ruby/object:Gem::Version
|
47
75
|
version: '0'
|
48
76
|
type: :development
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
|
-
- -
|
80
|
+
- - ">="
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rspec
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
58
86
|
requirements:
|
59
|
-
- - ~>
|
87
|
+
- - "~>"
|
60
88
|
- !ruby/object:Gem::Version
|
61
89
|
version: 2.14.1
|
62
90
|
type: :development
|
63
91
|
prerelease: false
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
65
93
|
requirements:
|
66
|
-
- - ~>
|
94
|
+
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: 2.14.1
|
69
97
|
description:
|
70
98
|
email:
|
71
99
|
- naoty.k@gmail.com
|
72
|
-
executables:
|
100
|
+
executables:
|
101
|
+
- graphdown
|
73
102
|
extensions: []
|
74
103
|
extra_rdoc_files: []
|
75
104
|
files:
|
76
|
-
- .gitignore
|
105
|
+
- ".gitignore"
|
106
|
+
- ".travis.yml"
|
77
107
|
- Gemfile
|
78
108
|
- LICENSE.txt
|
79
109
|
- README.md
|
80
110
|
- Rakefile
|
81
|
-
-
|
82
|
-
- examples/
|
83
|
-
- examples/
|
84
|
-
- examples/
|
111
|
+
- bin/graphdown
|
112
|
+
- examples/path_to_graphdown.html
|
113
|
+
- examples/path_to_graphdown.md
|
114
|
+
- examples/path_to_graphdown.png
|
115
|
+
- examples/servers_arrangement.html
|
116
|
+
- examples/servers_arrangement.md
|
117
|
+
- examples/servers_arrangement.png
|
85
118
|
- graphdown.gemspec
|
86
119
|
- lib/graphdown.rb
|
120
|
+
- lib/graphdown/cli.rb
|
121
|
+
- lib/graphdown/edge.rb
|
122
|
+
- lib/graphdown/graph.rb
|
123
|
+
- lib/graphdown/node.rb
|
124
|
+
- lib/graphdown/parser.rb
|
125
|
+
- lib/graphdown/point.rb
|
87
126
|
- lib/graphdown/renderable.rb
|
127
|
+
- lib/graphdown/version.rb
|
88
128
|
- spec/fixtures/sample.md
|
129
|
+
- spec/fixtures/sample.svg
|
89
130
|
- spec/fixtures/sample_without_graph.md
|
131
|
+
- spec/graphdown/graph_spec.rb
|
132
|
+
- spec/graphdown/node_spec.rb
|
133
|
+
- spec/graphdown/parser_spec.rb
|
90
134
|
- spec/graphdown/renderable_spec.rb
|
91
135
|
- spec/spec_helper.rb
|
92
136
|
homepage: https://github.com/naoty/graphdown
|
@@ -99,22 +143,26 @@ require_paths:
|
|
99
143
|
- lib
|
100
144
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
145
|
requirements:
|
102
|
-
- -
|
146
|
+
- - ">="
|
103
147
|
- !ruby/object:Gem::Version
|
104
148
|
version: '0'
|
105
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
150
|
requirements:
|
107
|
-
- -
|
151
|
+
- - ">="
|
108
152
|
- !ruby/object:Gem::Version
|
109
153
|
version: '0'
|
110
154
|
requirements: []
|
111
155
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.0
|
156
|
+
rubygems_version: 2.2.0
|
113
157
|
signing_key:
|
114
158
|
specification_version: 4
|
115
159
|
summary: Markdown extension for embedding graphs.
|
116
160
|
test_files:
|
117
161
|
- spec/fixtures/sample.md
|
162
|
+
- spec/fixtures/sample.svg
|
118
163
|
- spec/fixtures/sample_without_graph.md
|
164
|
+
- spec/graphdown/graph_spec.rb
|
165
|
+
- spec/graphdown/node_spec.rb
|
166
|
+
- spec/graphdown/parser_spec.rb
|
119
167
|
- spec/graphdown/renderable_spec.rb
|
120
168
|
- spec/spec_helper.rb
|
data/examples/parser.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require "redcarpet"
|
2
|
-
require "graphdown"
|
3
|
-
|
4
|
-
class BaseRenderer < Redcarpet::Render::HTML
|
5
|
-
include Graphdown::Renderable
|
6
|
-
end
|
7
|
-
|
8
|
-
markdown = Redcarpet::Markdown.new(BaseRenderer, fenced_code_blocks: true)
|
9
|
-
File.open("sample.md", "rb") do |file|
|
10
|
-
content = file.read
|
11
|
-
html = markdown.render(content)
|
12
|
-
File.open("sample.html", "wb") { |file| file.write(html) }
|
13
|
-
end
|
data/examples/sample.html
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
<h1>Views transition</h1>
|
2
|
-
<img src="/Users/naoty/workspace/ruby/graphdown/examples/sample.png"/>
|
3
|
-
<ul>
|
4
|
-
<li>Users visit show.html from index.html.</li>
|
5
|
-
<li>Users visit index.html from show.html.</li>
|
6
|
-
<li>Users visit new.html from index.html.</li>
|
7
|
-
<li>Users are redirected to index.html from new.html.</li>
|
8
|
-
</ul>
|
data/examples/sample.md
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# Views transition
|
2
|
-
|
3
|
-
```dot
|
4
|
-
digraph sample {
|
5
|
-
A [label = "index.html"];
|
6
|
-
B [label = "show.html"];
|
7
|
-
C [label = "new.html"];
|
8
|
-
|
9
|
-
A -> B [dir = both];
|
10
|
-
A -> C;
|
11
|
-
C -> A [label = "redirect"];
|
12
|
-
}
|
13
|
-
```
|
14
|
-
|
15
|
-
- Users visit show.html from index.html.
|
16
|
-
- Users visit index.html from show.html.
|
17
|
-
- Users visit new.html from index.html.
|
18
|
-
- Users are redirected to index.html from new.html.
|
data/examples/sample.png
DELETED
Binary file
|