asciidoctor-plantuml 0.0.9 → 0.0.10
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/lib/asciidoctor-plantuml.rb +3 -1
- data/lib/asciidoctor_plantuml/plantuml.rb +271 -0
- data/lib/asciidoctor_plantuml/version.rb +7 -0
- data/test/test_plantuml.rb +145 -160
- metadata +23 -9
- data/lib/asciidoctor-plantuml/plantuml.rb +0 -286
- data/lib/asciidoctor-plantuml/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 368d0190d9b9251c4a9794b9cd44ce6928cf8f963da4816d2a644d9f79bf5219
|
4
|
+
data.tar.gz: d5b7d4fc0527d9651770859163a18445e929616debfa1e3272864cd2a1dd2904
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0004181c00fa36e64b216dadf7b2b04f4fea160cb2b7795ad516458757b02e50c226da6a05e58644d4467e9df09c62004baf7c09797f7365bc9533b1a303cb86'
|
7
|
+
data.tar.gz: 61625a5f4c1c753158567c12a85f18c62763ccd434270dc2f4bc65273fcefe830af209b7d1e4f07a30ec2bdb9069b64c78aaf28b37520fe2b5b7eb00c553ba26
|
data/lib/asciidoctor-plantuml.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'asciidoctor'
|
2
4
|
require 'asciidoctor/extensions'
|
3
|
-
require_relative '
|
5
|
+
require_relative 'asciidoctor_plantuml/plantuml'
|
4
6
|
|
5
7
|
Asciidoctor::Extensions.register do
|
6
8
|
block Asciidoctor::PlantUml::BlockProcessor, :plantuml
|
@@ -0,0 +1,271 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'zlib'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'net/http'
|
8
|
+
|
9
|
+
module Asciidoctor
|
10
|
+
# PlantUML Module
|
11
|
+
module PlantUml
|
12
|
+
# PlantUML Configuration
|
13
|
+
class Configuration
|
14
|
+
DEFAULT_URL = ENV['PLANTUML_URL'] || ''
|
15
|
+
|
16
|
+
attr_accessor :url, :txt_enable, :svg_enable, :png_enable
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@url = DEFAULT_URL
|
20
|
+
@txt_enable = true
|
21
|
+
@svg_enable = true
|
22
|
+
@png_enable = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_writer :configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.configuration
|
31
|
+
@configuration ||= Configuration.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.configure
|
35
|
+
yield(configuration)
|
36
|
+
end
|
37
|
+
|
38
|
+
# PlantUML Processor
|
39
|
+
class Processor
|
40
|
+
FORMATS = %w[png svg txt].freeze
|
41
|
+
DEFAULT_FORMAT = FORMATS[0]
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def valid_format?(format)
|
45
|
+
FORMATS.include?(format)
|
46
|
+
end
|
47
|
+
|
48
|
+
def server_url
|
49
|
+
PlantUml.configuration.url
|
50
|
+
end
|
51
|
+
|
52
|
+
def txt_enabled?
|
53
|
+
PlantUml.configuration.txt_enable
|
54
|
+
end
|
55
|
+
|
56
|
+
def png_enabled?
|
57
|
+
PlantUml.configuration.png_enable
|
58
|
+
end
|
59
|
+
|
60
|
+
def svg_enabled?
|
61
|
+
PlantUml.configuration.svg_enable
|
62
|
+
end
|
63
|
+
|
64
|
+
def enabled?
|
65
|
+
txt_enabled? || png_enabled? || svg_enabled?
|
66
|
+
end
|
67
|
+
|
68
|
+
def plantuml_content_format(code, format, attrs = {})
|
69
|
+
if %w[png svg].include?(format)
|
70
|
+
plantuml_img_content(code, format, attrs)
|
71
|
+
elsif format == 'txt' && txt_enabled?
|
72
|
+
plantuml_txt_content(code, format, attrs)
|
73
|
+
else
|
74
|
+
plantuml_invalid_content(format, attrs)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def plantuml_content(code, attrs = {})
|
79
|
+
format = attrs['format'] || DEFAULT_FORMAT
|
80
|
+
|
81
|
+
return plantuml_disabled_content(code, attrs) unless enabled?
|
82
|
+
|
83
|
+
unless valid_uri?(server_url)
|
84
|
+
return plantuml_server_unavailable_content(server_url, attrs)
|
85
|
+
end
|
86
|
+
|
87
|
+
plantuml_content_format(code, format, attrs)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Compression code used to generate PlantUML URLs. Taken directly from
|
91
|
+
# the transcoder class in the PlantUML java code.
|
92
|
+
def gen_url(text, format)
|
93
|
+
result = ''
|
94
|
+
compressed_data = Zlib::Deflate.deflate(text)
|
95
|
+
compressed_data.chars.each_slice(3) do |bytes|
|
96
|
+
# print bytes[0], ' ' , bytes[1] , ' ' , bytes[2]
|
97
|
+
b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF)
|
98
|
+
b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF)
|
99
|
+
b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF)
|
100
|
+
result += append3bytes(b1, b2, b3)
|
101
|
+
end
|
102
|
+
join_paths(server_url, "#{format}/", result).to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def plantuml_txt_content(code, format, attrs = {})
|
108
|
+
url = gen_url(code, format)
|
109
|
+
URI(url).open do |f|
|
110
|
+
plantuml_ascii_content(f.read, attrs)
|
111
|
+
end
|
112
|
+
rescue OpenURI::HTTPError, Errno::ECONNREFUSED, SocketError
|
113
|
+
plantuml_img_content(code, format, attrs)
|
114
|
+
end
|
115
|
+
|
116
|
+
def plantuml_ascii_content(code, attrs = {})
|
117
|
+
content = '<div class="listingblock">'
|
118
|
+
content += '<div class="content">'
|
119
|
+
content += '<pre '
|
120
|
+
content += "id=\"#{attrs['id']}\" " if attrs['id']
|
121
|
+
content += 'class="plantuml">\n'
|
122
|
+
content += code
|
123
|
+
content += '</pre>'
|
124
|
+
content += '</div>'
|
125
|
+
content + '</div>'
|
126
|
+
end
|
127
|
+
|
128
|
+
def plantuml_img_content(code, format, attrs = {})
|
129
|
+
content = '<div class="imageblock">'
|
130
|
+
content += '<div class="content">'
|
131
|
+
content += '<img '
|
132
|
+
content += "id=\"#{attrs['id']}\" " if attrs['id']
|
133
|
+
content += 'class="plantuml" '
|
134
|
+
content += "width=\"#{attrs['width']}\" " if attrs['width']
|
135
|
+
content += "height=\"#{attrs['height']}\" " if attrs['height']
|
136
|
+
content += "alt=\"#{attrs['alt']}\" " if attrs['alt']
|
137
|
+
content += "src=\"#{gen_url(code, format)}\" />"
|
138
|
+
content += '</div>'
|
139
|
+
content + '</div>'
|
140
|
+
end
|
141
|
+
|
142
|
+
def plantuml_invalid_content(format, attrs = {})
|
143
|
+
content = '<div class="listingblock">'
|
144
|
+
content += '<div class="content">'
|
145
|
+
content += '<pre '
|
146
|
+
content += "id=\"#{attrs['id']}\" " if attrs['id']
|
147
|
+
content += 'class="plantuml plantuml-error"> '
|
148
|
+
content += "PlantUML Error: Invalid format \"#{format}\""
|
149
|
+
content += '</pre>'
|
150
|
+
content += '</div>'
|
151
|
+
content + '</div>'
|
152
|
+
end
|
153
|
+
|
154
|
+
def plantuml_server_unavailable_content(url, attrs = {})
|
155
|
+
content = '<div class="listingblock">'
|
156
|
+
content += '<div class="content">'
|
157
|
+
content += '<pre '
|
158
|
+
content += "id=\"#{attrs['id']}\" " if attrs['id']
|
159
|
+
content += 'class="plantuml plantuml-error"> '
|
160
|
+
content += "Error: cannot connect to PlantUML server at \"#{url}\""
|
161
|
+
content += '</pre>'
|
162
|
+
content += '</div>'
|
163
|
+
content + '</div>'
|
164
|
+
end
|
165
|
+
|
166
|
+
def plantuml_disabled_content(code, attrs = {})
|
167
|
+
content = '<div class="listingblock">'
|
168
|
+
content += '<div class="content">'
|
169
|
+
content += '<pre '
|
170
|
+
content += "id=\"#{attrs['id']}\" " if attrs['id']
|
171
|
+
content += 'class="plantuml plantuml-error">\n'
|
172
|
+
content += code
|
173
|
+
content += '</pre>'
|
174
|
+
content += '</div>'
|
175
|
+
content + '</div>'
|
176
|
+
end
|
177
|
+
|
178
|
+
def encode6bit(bit)
|
179
|
+
return ('0'.ord + bit).chr if bit < 10
|
180
|
+
|
181
|
+
bit -= 10
|
182
|
+
return ('A'.ord + bit).chr if bit < 26
|
183
|
+
|
184
|
+
bit -= 26
|
185
|
+
return ('a'.ord + bit).chr if bit < 26
|
186
|
+
|
187
|
+
bit -= 26
|
188
|
+
return '-' if bit.zero?
|
189
|
+
|
190
|
+
return '_' if bit == 1
|
191
|
+
|
192
|
+
'?'
|
193
|
+
end
|
194
|
+
|
195
|
+
def append3bytes(bit1, bit2, bit3)
|
196
|
+
c1 = bit1 >> 2
|
197
|
+
c2 = ((bit1 & 0x3) << 4) | (bit2 >> 4)
|
198
|
+
c3 = ((bit2 & 0xF) << 2) | (bit3 >> 6)
|
199
|
+
c4 = bit3 & 0x3F
|
200
|
+
encode6bit(c1 & 0x3F).chr +
|
201
|
+
encode6bit(c2 & 0x3F).chr +
|
202
|
+
encode6bit(c3 & 0x3F).chr +
|
203
|
+
encode6bit(c4 & 0x3F).chr
|
204
|
+
end
|
205
|
+
|
206
|
+
# Make a call to the PlantUML server with the simplest diagram possible
|
207
|
+
# to check if the server is available or not.
|
208
|
+
def check_server(check_url)
|
209
|
+
response = Net::HTTP.get_response(URI(check_url))
|
210
|
+
response.is_a?(Net::HTTPSuccess)
|
211
|
+
rescue OpenURI::HTTPError, Errno::ECONNREFUSED, SocketError
|
212
|
+
false
|
213
|
+
end
|
214
|
+
|
215
|
+
def valid_uri?(uri)
|
216
|
+
!(uri =~ /\A#{URI.regexp(%w[http https])}\z/).nil?
|
217
|
+
end
|
218
|
+
|
219
|
+
def join_paths(*paths, separator: '/')
|
220
|
+
paths = paths.compact.reject(&:empty?)
|
221
|
+
last = paths.length - 1
|
222
|
+
paths.each_with_index.map do |path, index|
|
223
|
+
expand_path(path, index, last, separator)
|
224
|
+
end.join
|
225
|
+
end
|
226
|
+
|
227
|
+
def expand_path(path, current, last, separator)
|
228
|
+
path = path[1..-1] if path.start_with?(separator) && current.zero?
|
229
|
+
|
230
|
+
unless path.end_with?(separator) || current == last
|
231
|
+
path = [path, separator]
|
232
|
+
end
|
233
|
+
|
234
|
+
path
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# PlantUML BlockProcessor
|
240
|
+
class BlockProcessor < Asciidoctor::Extensions::BlockProcessor
|
241
|
+
use_dsl
|
242
|
+
named :plantuml
|
243
|
+
on_context :listing
|
244
|
+
content_model :simple
|
245
|
+
|
246
|
+
def process(parent, target, attrs)
|
247
|
+
lines = target.lines
|
248
|
+
|
249
|
+
unless target.lines[0] =~ /@startuml/
|
250
|
+
lines = ['@startuml'] + target.lines
|
251
|
+
end
|
252
|
+
|
253
|
+
lines += ['@enduml'] unless target.lines[-1] =~ /@enduml/
|
254
|
+
|
255
|
+
content = Processor.plantuml_content(lines.join("\n"), attrs)
|
256
|
+
|
257
|
+
create_plantuml_block(parent, content, attrs)
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
def create_plantuml_block(parent, content, attrs)
|
263
|
+
Asciidoctor::Block.new parent, :pass, {
|
264
|
+
content_model: :raw,
|
265
|
+
source: content,
|
266
|
+
subs: :default
|
267
|
+
}.merge(attrs)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
data/test/test_plantuml.rb
CHANGED
@@ -1,133 +1,133 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
[
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
[
|
38
|
-
|
39
|
-
[COMP1]
|
40
|
-
[COMP2]
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'asciidoctor'
|
5
|
+
require 'stringio'
|
6
|
+
require 'nokogiri'
|
7
|
+
require 'asciidoctor-plantuml'
|
8
|
+
|
9
|
+
DOC_BASIC = <<~ENDOFSTRING
|
10
|
+
= Hello PlantUML!
|
11
|
+
|
12
|
+
[plantuml, format="png"]
|
13
|
+
.Title Of this
|
14
|
+
----
|
15
|
+
User -> (Start)
|
16
|
+
User --> (Use the application) : Label
|
17
|
+
----
|
18
|
+
ENDOFSTRING
|
19
|
+
|
20
|
+
DOC_BASIC2 = <<~ENDOFSTRING
|
21
|
+
= Hello PlantUML!
|
22
|
+
|
23
|
+
[plantuml, format="png"]
|
24
|
+
.Title Of this
|
25
|
+
[[fig-xref]]
|
26
|
+
----
|
27
|
+
@startuml
|
28
|
+
User -> (Start)
|
29
|
+
User --> (Use the application) : Label
|
30
|
+
@enduml
|
31
|
+
----
|
32
|
+
ENDOFSTRING
|
33
|
+
|
34
|
+
DOC_BASIC3 = <<~ENDOFSTRING
|
35
|
+
= Hello Compound PlantUML!
|
36
|
+
|
37
|
+
[plantuml, format="png"]
|
38
|
+
----
|
39
|
+
[COMP1]
|
40
|
+
[COMP2]
|
41
|
+
[COMP1] -> [COMP2]
|
42
|
+
[COMP2] --> [COMP3]
|
43
|
+
----
|
44
|
+
ENDOFSTRING
|
45
|
+
|
46
|
+
DOC_ID = <<~ENDOFSTRING
|
47
|
+
= Hello PlantUML!
|
48
|
+
|
49
|
+
[plantuml, format="png", id="myId"]
|
50
|
+
----
|
51
|
+
User -> (Start)
|
52
|
+
User --> (Use the application) : Label
|
53
|
+
----
|
54
|
+
ENDOFSTRING
|
55
|
+
|
56
|
+
DOC_DIM = <<~ENDOFSTRING
|
57
|
+
= Hello PlantUML!
|
58
|
+
|
59
|
+
[plantuml, format="png", width="100px", height="50px"]
|
60
|
+
----
|
61
|
+
User -> (Start)
|
62
|
+
User --> (Use the application) : Label
|
63
|
+
----
|
64
|
+
ENDOFSTRING
|
65
|
+
|
66
|
+
DOC_ALT = <<~ENDOFSTRING
|
67
|
+
= Hello PlantUML!
|
68
|
+
|
69
|
+
[plantuml, format="png", alt="alt"]
|
70
|
+
----
|
71
|
+
User -> (Start)
|
72
|
+
User --> (Use the application) : Label
|
73
|
+
----
|
74
|
+
ENDOFSTRING
|
75
|
+
|
76
|
+
DOC_BAD_FORMAT = <<~ENDOFSTRING
|
77
|
+
= Hello PlantUML!
|
78
|
+
|
79
|
+
[plantuml, format="jpg"]
|
80
|
+
----
|
81
|
+
User -> (Start)
|
82
|
+
User --> (Use the application) : Label
|
83
|
+
----
|
84
|
+
ENDOFSTRING
|
85
|
+
|
86
|
+
DOC_MULTI = <<~ENDOFSTRING
|
87
|
+
= Hello PlantUML!
|
88
|
+
|
89
|
+
[plantuml, format="png"]
|
90
|
+
----
|
91
|
+
User -> (Start)
|
92
|
+
User --> (Use the application) : Label
|
93
|
+
----
|
94
|
+
|
95
|
+
[plantuml, format="png"]
|
96
|
+
----
|
97
|
+
User -> (Start)
|
98
|
+
User --> (Use the application) : Label
|
99
|
+
----
|
100
|
+
|
101
|
+
[plantuml, format="txt"]
|
102
|
+
----
|
103
|
+
User -> (Start)
|
104
|
+
User --> (Use the application) : Label
|
105
|
+
----
|
106
|
+
ENDOFSTRING
|
107
|
+
|
108
|
+
DOC_TXT = <<~ENDOFSTRING
|
109
|
+
= Hello PlantUML!
|
110
|
+
|
111
|
+
[plantuml, format="txt"]
|
112
|
+
----
|
113
|
+
User -> (Start)
|
114
|
+
User --> (Use the application) : Label
|
115
|
+
----
|
116
|
+
ENDOFSTRING
|
115
117
|
|
116
118
|
class PlantUmlTest < Test::Unit::TestCase
|
117
|
-
|
118
|
-
|
119
|
-
GENURL2 = "http://localhost:8080/plantuml/png/U9npA2v9B2efpStXYdRszmqmZ8NGHh4mleAkdGAAa15G22Pc7Clba9gN0jGE00W75Cm0"
|
119
|
+
GENURL = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStX2YrEBLBGjLFG20Q9Q4Bv804WIw4a8rKXiQ0W9pCviIGpFqzJmKh19p4fDOVB8JKl1QWT05kd5wq0'
|
120
|
+
GENURL2 = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStXYdRszmqmZ8NGHh4mleAkdGAAa15G22Pc7Clba9gN0jGE00W75Cm0'
|
120
121
|
|
121
122
|
def setup
|
122
123
|
Asciidoctor::PlantUml.configure do |c|
|
123
|
-
c.url =
|
124
|
+
c.url = 'http://localhost:8080/plantuml'
|
124
125
|
c.txt_enable = true
|
125
126
|
end
|
126
127
|
end
|
127
128
|
|
128
129
|
def test_plantuml_block_processor
|
129
|
-
|
130
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: "html5")
|
130
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5')
|
131
131
|
page = Nokogiri::HTML(html)
|
132
132
|
|
133
133
|
elements = page.css('img.plantuml')
|
@@ -136,11 +136,11 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
136
136
|
|
137
137
|
element = elements.first
|
138
138
|
|
139
|
-
assert_equal GENURL, element[
|
139
|
+
assert_equal GENURL, element['src']
|
140
140
|
end
|
141
141
|
|
142
142
|
def test_plantuml_block_processor2
|
143
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC2), backend:
|
143
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC2), backend: 'html5')
|
144
144
|
page = Nokogiri::HTML(html)
|
145
145
|
|
146
146
|
elements = page.css('img.plantuml')
|
@@ -149,11 +149,11 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
149
149
|
|
150
150
|
element = elements.first
|
151
151
|
|
152
|
-
assert_equal GENURL, element[
|
152
|
+
assert_equal GENURL, element['src']
|
153
153
|
end
|
154
154
|
|
155
155
|
def test_plantuml_block_processor3
|
156
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC3), backend:
|
156
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC3), backend: 'html5')
|
157
157
|
page = Nokogiri::HTML(html)
|
158
158
|
|
159
159
|
elements = page.css('img.plantuml')
|
@@ -162,12 +162,11 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
162
162
|
|
163
163
|
element = elements.first
|
164
164
|
|
165
|
-
assert_equal GENURL2, element[
|
165
|
+
assert_equal GENURL2, element['src']
|
166
166
|
end
|
167
167
|
|
168
168
|
def test_plantuml_id_attribute
|
169
|
-
|
170
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_ID), backend: "html5")
|
169
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_ID), backend: 'html5')
|
171
170
|
page = Nokogiri::HTML(html)
|
172
171
|
|
173
172
|
elements = page.css('img.plantuml')
|
@@ -175,13 +174,11 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
175
174
|
assert_equal elements.size, 1
|
176
175
|
element = elements.first
|
177
176
|
|
178
|
-
assert_equal
|
179
|
-
|
177
|
+
assert_equal 'myId', element['id']
|
180
178
|
end
|
181
179
|
|
182
180
|
def test_plantuml_dimension_attribute
|
183
|
-
|
184
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_DIM), backend: "html5")
|
181
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_DIM), backend: 'html5')
|
185
182
|
page = Nokogiri::HTML(html)
|
186
183
|
|
187
184
|
elements = page.css('img.plantuml')
|
@@ -189,14 +186,12 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
189
186
|
assert_equal elements.size, 1
|
190
187
|
element = elements.first
|
191
188
|
|
192
|
-
assert_equal
|
193
|
-
assert_equal
|
194
|
-
|
189
|
+
assert_equal '100px', element['width']
|
190
|
+
assert_equal '50px', element['height']
|
195
191
|
end
|
196
192
|
|
197
193
|
def test_plantuml_alt_attribute
|
198
|
-
|
199
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_ALT), backend: "html5")
|
194
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_ALT), backend: 'html5')
|
200
195
|
page = Nokogiri::HTML(html)
|
201
196
|
|
202
197
|
elements = page.css('img.plantuml')
|
@@ -204,23 +199,20 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
204
199
|
assert_equal elements.size, 1
|
205
200
|
element = elements.first
|
206
201
|
|
207
|
-
assert_equal
|
208
|
-
|
202
|
+
assert_equal 'alt', element['alt']
|
209
203
|
end
|
210
204
|
|
211
205
|
def test_should_show_bad_format
|
212
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BAD_FORMAT), backend:
|
206
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BAD_FORMAT), backend: 'html5')
|
213
207
|
|
214
208
|
page = Nokogiri::HTML(html)
|
215
209
|
|
216
210
|
elements = page.css('pre.plantuml-error')
|
217
211
|
assert_equal elements.size, 1
|
218
|
-
|
219
212
|
end
|
220
213
|
|
221
214
|
def test_plantuml_multiple
|
222
|
-
|
223
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend: "html5")
|
215
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend: 'html5')
|
224
216
|
page = Nokogiri::HTML(html)
|
225
217
|
|
226
218
|
elements = page.css('img.plantuml')
|
@@ -228,72 +220,65 @@ class PlantUmlTest < Test::Unit::TestCase
|
|
228
220
|
|
229
221
|
elements = page.css('.plantuml-error')
|
230
222
|
assert_equal elements.size, 0
|
231
|
-
|
232
223
|
end
|
233
224
|
|
234
225
|
def test_plantuml_bad_server
|
235
|
-
|
236
226
|
Asciidoctor::PlantUml.configure do |c|
|
237
|
-
c.url =
|
227
|
+
c.url = 'http://nonexistent.com/plantuml'
|
238
228
|
end
|
239
229
|
|
240
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend:
|
230
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend: 'html5')
|
241
231
|
page = Nokogiri::HTML(html)
|
242
232
|
|
243
233
|
elements = page.css('img.plantuml')
|
244
|
-
assert_equal elements.size
|
234
|
+
assert_equal 3, elements.size
|
245
235
|
|
246
236
|
elements = page.css('.plantuml-error')
|
247
|
-
assert_equal elements.size
|
237
|
+
assert_equal 0, elements.size
|
248
238
|
end
|
249
239
|
|
250
240
|
def test_plantuml_invalid_uri
|
251
|
-
|
252
241
|
Asciidoctor::PlantUml.configure do |c|
|
253
|
-
c.url =
|
242
|
+
c.url = 'ftp://test.com'
|
254
243
|
end
|
255
244
|
|
256
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend:
|
245
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5')
|
257
246
|
page = Nokogiri::HTML(html)
|
258
247
|
elements = page.css('pre.plantuml-error')
|
259
248
|
assert_equal elements.size, 1
|
260
249
|
end
|
261
250
|
|
262
251
|
def test_plantuml_nil_uri
|
263
|
-
|
264
252
|
Asciidoctor::PlantUml.configure do |c|
|
265
253
|
c.url = nil
|
266
254
|
end
|
267
255
|
|
268
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend:
|
256
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5')
|
269
257
|
page = Nokogiri::HTML(html)
|
270
258
|
elements = page.css('pre.plantuml-error')
|
271
259
|
assert_equal elements.size, 1
|
272
260
|
end
|
273
261
|
|
274
262
|
def test_plantuml_empty_uri
|
275
|
-
|
276
263
|
Asciidoctor::PlantUml.configure do |c|
|
277
|
-
c.url =
|
264
|
+
c.url = ''
|
278
265
|
end
|
279
266
|
|
280
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend:
|
267
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5')
|
281
268
|
page = Nokogiri::HTML(html)
|
282
269
|
elements = page.css('pre.plantuml-error')
|
283
270
|
assert_equal elements.size, 1
|
284
271
|
end
|
285
272
|
|
286
273
|
def test_disable_txt
|
287
|
-
|
288
274
|
Asciidoctor::PlantUml.configure do |c|
|
289
|
-
c.url =
|
275
|
+
c.url = 'http://localhost:8080/plantuml'
|
290
276
|
c.txt_enable = false
|
291
277
|
end
|
292
278
|
|
293
|
-
html = ::Asciidoctor.convert(StringIO.new(DOC_TXT), backend:
|
279
|
+
html = ::Asciidoctor.convert(StringIO.new(DOC_TXT), backend: 'html5')
|
294
280
|
page = Nokogiri::HTML(html)
|
295
281
|
elements = page.css('pre.plantuml-error')
|
296
282
|
assert_equal elements.size, 1
|
297
|
-
|
298
283
|
end
|
299
284
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asciidoctor-plantuml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Horacio Sanson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,19 +53,19 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '10.5'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '0.66'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '0.66'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: test-unit
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,8 +108,8 @@ extensions: []
|
|
94
108
|
extra_rdoc_files: []
|
95
109
|
files:
|
96
110
|
- lib/asciidoctor-plantuml.rb
|
97
|
-
- lib/
|
98
|
-
- lib/
|
111
|
+
- lib/asciidoctor_plantuml/plantuml.rb
|
112
|
+
- lib/asciidoctor_plantuml/version.rb
|
99
113
|
- test/test_plantuml.rb
|
100
114
|
homepage: https://github.com/hsanson/asciidoctor-plantuml
|
101
115
|
licenses:
|
@@ -109,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
123
|
requirements:
|
110
124
|
- - ">="
|
111
125
|
- !ruby/object:Gem::Version
|
112
|
-
version: '2.
|
126
|
+
version: '2.3'
|
113
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - ">="
|
@@ -120,6 +134,6 @@ rubyforge_project:
|
|
120
134
|
rubygems_version: 2.7.6
|
121
135
|
signing_key:
|
122
136
|
specification_version: 4
|
123
|
-
summary:
|
137
|
+
summary: Asciidoctor support for PlantUML diagrams.
|
124
138
|
test_files:
|
125
139
|
- test/test_plantuml.rb
|
@@ -1,286 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'zlib'
|
3
|
-
require 'open-uri'
|
4
|
-
require 'net/http'
|
5
|
-
|
6
|
-
module Asciidoctor
|
7
|
-
module PlantUml
|
8
|
-
|
9
|
-
class Configuration
|
10
|
-
|
11
|
-
DEFAULT_URL = ENV["PLANTUML_URL"] || ""
|
12
|
-
|
13
|
-
attr_accessor :url, :txt_enable, :svg_enable, :png_enable
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@url = DEFAULT_URL
|
17
|
-
@txt_enable = true
|
18
|
-
@svg_enable = true
|
19
|
-
@png_enable = true
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class << self
|
24
|
-
attr_writer :configuration
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.configuration
|
28
|
-
@configuration ||= Configuration.new
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.configure
|
32
|
-
yield(configuration)
|
33
|
-
end
|
34
|
-
|
35
|
-
class Processor
|
36
|
-
|
37
|
-
FORMATS = ["png", "svg", "txt"]
|
38
|
-
DEFAULT_FORMAT = FORMATS[0]
|
39
|
-
|
40
|
-
class << self
|
41
|
-
def valid_format?(format)
|
42
|
-
FORMATS.include?(format)
|
43
|
-
end
|
44
|
-
|
45
|
-
def server_url
|
46
|
-
PlantUml::configuration.url
|
47
|
-
end
|
48
|
-
|
49
|
-
def txt_enabled?
|
50
|
-
PlantUml::configuration.txt_enable
|
51
|
-
end
|
52
|
-
|
53
|
-
def png_enabled?
|
54
|
-
PlantUml::configuration.png_enable
|
55
|
-
end
|
56
|
-
|
57
|
-
def svg_enabled?
|
58
|
-
PlantUml::configuration.svg_enable
|
59
|
-
end
|
60
|
-
|
61
|
-
def enabled?
|
62
|
-
txt_enabled? || png_enabled? || svg_enabled?
|
63
|
-
end
|
64
|
-
|
65
|
-
def plantuml_content(code, attrs = {})
|
66
|
-
|
67
|
-
format = attrs["format"] || DEFAULT_FORMAT
|
68
|
-
|
69
|
-
if !enabled?
|
70
|
-
return plantuml_disabled_content(code, attrs)
|
71
|
-
end
|
72
|
-
|
73
|
-
if !valid_uri?(server_url)
|
74
|
-
return plantuml_server_unavailable_content(server_url, attrs)
|
75
|
-
end
|
76
|
-
|
77
|
-
case format
|
78
|
-
when "png"
|
79
|
-
plantuml_img_content(code, format, attrs)
|
80
|
-
when "txt"
|
81
|
-
if txt_enabled?
|
82
|
-
plantuml_txt_content(code, format, attrs)
|
83
|
-
else
|
84
|
-
plantuml_invalid_content(format, attrs)
|
85
|
-
end
|
86
|
-
when "svg"
|
87
|
-
plantuml_img_content(code, format, attrs)
|
88
|
-
else
|
89
|
-
plantuml_invalid_content(format, attrs)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Compression code used to generate PlantUML URLs. Taken directly from the
|
94
|
-
# Transcoder class in the PlantUML java code.
|
95
|
-
def gen_url(text, format)
|
96
|
-
result = ""
|
97
|
-
compressedData = Zlib::Deflate.deflate(text)
|
98
|
-
compressedData.chars.each_slice(3) do |bytes|
|
99
|
-
#print bytes[0], ' ' , bytes[1] , ' ' , bytes[2]
|
100
|
-
b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF)
|
101
|
-
b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF)
|
102
|
-
b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF)
|
103
|
-
result += append3bytes(b1, b2, b3)
|
104
|
-
end
|
105
|
-
join_paths(server_url, "/#{format}/", result).to_s
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def plantuml_txt_content(code, format, attrs = {})
|
111
|
-
begin
|
112
|
-
url = gen_url(code, format)
|
113
|
-
open(url) do |f|
|
114
|
-
plantuml_ascii_content(f.read, format, attrs)
|
115
|
-
end
|
116
|
-
rescue
|
117
|
-
plantuml_img_content(code, format, attrs)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def plantuml_ascii_content(code, format, attrs = {})
|
122
|
-
content = "<div class=\"listingblock\">"
|
123
|
-
content += "<div class=\"content\">"
|
124
|
-
content += "<pre "
|
125
|
-
content +="id=\"#{attrs['id']}\" " if attrs['id']
|
126
|
-
content +="class=\"plantuml\">\n"
|
127
|
-
content += code
|
128
|
-
content +="</pre>"
|
129
|
-
content += "</div>"
|
130
|
-
content += "</div>"
|
131
|
-
end
|
132
|
-
|
133
|
-
def plantuml_img_content(code, format, attrs = {})
|
134
|
-
content = "<div class=\"imageblock\">"
|
135
|
-
content += "<div class=\"content\">"
|
136
|
-
content += "<img "
|
137
|
-
content +="id=\"#{attrs['id']}\" " if attrs['id']
|
138
|
-
content +="class=\"plantuml\" "
|
139
|
-
content +="width=\"#{attrs['width']}\" " if attrs['width']
|
140
|
-
content +="height=\"#{attrs['height']}\" " if attrs['height']
|
141
|
-
content +="alt=\"#{attrs['alt']}\" " if attrs['alt']
|
142
|
-
content +="src=\"#{gen_url(code, format)}\" />"
|
143
|
-
content += "</div>"
|
144
|
-
content += "</div>"
|
145
|
-
end
|
146
|
-
|
147
|
-
def plantuml_invalid_content(format, attrs = {})
|
148
|
-
content = "<div class=\"listingblock\">"
|
149
|
-
content += "<div class=\"content\">"
|
150
|
-
content += "<pre "
|
151
|
-
content +="id=\"#{attrs['id']}\" " if attrs['id']
|
152
|
-
content +="class=\"plantuml plantuml-error\"> "
|
153
|
-
content += "PlantUML Error: Invalid format \"#{format}\""
|
154
|
-
content +="</pre>"
|
155
|
-
content += "</div>"
|
156
|
-
content += "</div>"
|
157
|
-
end
|
158
|
-
|
159
|
-
def plantuml_server_unavailable_content(url, attrs = {})
|
160
|
-
content = "<div class=\"listingblock\">"
|
161
|
-
content += "<div class=\"content\">"
|
162
|
-
content += "<pre "
|
163
|
-
content +="id=\"#{attrs['id']}\" " if attrs['id']
|
164
|
-
content +="class=\"plantuml plantuml-error\"> "
|
165
|
-
content += "PlantUML Error: cannot connect to PlantUML server at \"#{url}\""
|
166
|
-
content +="</pre>"
|
167
|
-
content += "</div>"
|
168
|
-
content += "</div>"
|
169
|
-
end
|
170
|
-
|
171
|
-
def plantuml_disabled_content(code, attrs = {})
|
172
|
-
content = "<div class=\"listingblock\">"
|
173
|
-
content += "<div class=\"content\">"
|
174
|
-
content += "<pre "
|
175
|
-
content +="id=\"#{attrs['id']}\" " if attrs['id']
|
176
|
-
content +="class=\"plantuml plantuml-error\">\n"
|
177
|
-
content += code
|
178
|
-
content +="</pre>"
|
179
|
-
content += "</div>"
|
180
|
-
content += "</div>"
|
181
|
-
end
|
182
|
-
|
183
|
-
def encode6bit(b)
|
184
|
-
if b < 10
|
185
|
-
return ('0'.ord + b).chr
|
186
|
-
end
|
187
|
-
b = b - 10
|
188
|
-
if b < 26
|
189
|
-
return ('A'.ord + b).chr
|
190
|
-
end
|
191
|
-
b = b - 26
|
192
|
-
if b < 26
|
193
|
-
return ('a'.ord + b).chr
|
194
|
-
end
|
195
|
-
b = b - 26
|
196
|
-
if b == 0
|
197
|
-
return '-'
|
198
|
-
end
|
199
|
-
if b == 1
|
200
|
-
return '_'
|
201
|
-
end
|
202
|
-
return '?'
|
203
|
-
end
|
204
|
-
|
205
|
-
def append3bytes(b1, b2, b3)
|
206
|
-
c1 = b1 >> 2
|
207
|
-
c2 = ((b1 & 0x3) << 4) | (b2 >> 4)
|
208
|
-
c3 = ((b2 & 0xF) << 2) | (b3 >> 6)
|
209
|
-
c4 = b3 & 0x3F
|
210
|
-
return encode6bit(c1 & 0x3F).chr +
|
211
|
-
encode6bit(c2 & 0x3F).chr +
|
212
|
-
encode6bit(c3 & 0x3F).chr +
|
213
|
-
encode6bit(c4 & 0x3F).chr
|
214
|
-
end
|
215
|
-
|
216
|
-
# Make a call to the PlantUML server with the simplest diagram possible to
|
217
|
-
# check if the server is available or not.
|
218
|
-
def check_server(check_url)
|
219
|
-
response = Net::HTTP.get_response(URI(check_url))
|
220
|
-
return response.is_a?(Net::HTTPSuccess)
|
221
|
-
rescue
|
222
|
-
return false
|
223
|
-
end
|
224
|
-
|
225
|
-
def valid_uri?(uri)
|
226
|
-
!(uri =~ /\A#{URI::regexp(['http', 'https'])}\z/).nil?
|
227
|
-
end
|
228
|
-
|
229
|
-
def join_paths(*paths, separator: '/')
|
230
|
-
paths = paths.compact.reject(&:empty?)
|
231
|
-
last = paths.length - 1
|
232
|
-
paths.each_with_index.map { |path, index|
|
233
|
-
expand_path(path, index, last, separator)
|
234
|
-
}.join
|
235
|
-
end
|
236
|
-
|
237
|
-
def expand_path(path, current, last, separator)
|
238
|
-
if path.start_with?(separator) && current != 0
|
239
|
-
path = path[1..-1]
|
240
|
-
end
|
241
|
-
|
242
|
-
unless path.end_with?(separator) || current == last
|
243
|
-
path = [path, separator]
|
244
|
-
end
|
245
|
-
|
246
|
-
path
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
class BlockProcessor < Asciidoctor::Extensions::BlockProcessor
|
252
|
-
|
253
|
-
use_dsl
|
254
|
-
named :plantuml
|
255
|
-
on_context :listing
|
256
|
-
content_model :simple
|
257
|
-
|
258
|
-
def process(parent, target, attrs)
|
259
|
-
|
260
|
-
lines = target.lines
|
261
|
-
|
262
|
-
if !(target.lines[0] =~ /@startuml/)
|
263
|
-
lines = ["@startuml"] + target.lines
|
264
|
-
end
|
265
|
-
|
266
|
-
if !(target.lines[-1] =~ /@enduml/)
|
267
|
-
lines += ["@enduml"]
|
268
|
-
end
|
269
|
-
|
270
|
-
content = Processor.plantuml_content(lines.join("\n"), attrs)
|
271
|
-
|
272
|
-
return create_plantuml_block(parent, content, attrs)
|
273
|
-
|
274
|
-
end
|
275
|
-
|
276
|
-
private
|
277
|
-
|
278
|
-
def create_plantuml_block(parent, content, attrs)
|
279
|
-
Asciidoctor::Block.new parent, :pass, { content_model: :raw,
|
280
|
-
source: content, subs: :default }.merge(attrs)
|
281
|
-
end
|
282
|
-
|
283
|
-
end
|
284
|
-
|
285
|
-
end
|
286
|
-
end
|