markdoc 1.1.0 → 2.0.0

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
+ SHA256:
3
+ metadata.gz: a313ccc3aef3a547c0d688e8a84dac9097c6fad2e7e5e27e46475311646e1e67
4
+ data.tar.gz: 4491396850eac63988494d4e68f110db83813304a9cc1bab00458eb5875e497e
5
+ SHA512:
6
+ metadata.gz: 949e3a80919e9b9b54f624b659c5bbb72ca0cf9b7746c9080dd8fd08a27b012147099cc2c50397d68694f05a717eb8265d14bb058146b8174416cb59dbce9db3
7
+ data.tar.gz: a32fbb8e268d9035ff15c4b2d93726e48a9bb5ab3fb9af6e13c95a7d8979f7b6ee57b1d469c23871c6179b3d2b83c0da9c6658351cf8932a66c6ecf777682f7b
data/bin/markdoc CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'markdoc'
4
5
 
data/bin/pseudo2svg CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Pseudo code to Graphviz converter
4
5
  #
data/bin/sequence2svg CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Sequence definition to image converter using sequence.pic macros.
4
5
  # http://www.umlgraph.org/doc/seq-intro.html
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'polyglot'
2
4
  require 'treetop'
3
5
 
@@ -13,14 +15,17 @@ module Markdoc
13
15
 
14
16
  class ActionLiteral < Treetop::Runtime::SyntaxNode
15
17
  def out(file)
16
- file.write %Q(#{id} [shape=box label="#{label}"]\n)
18
+ file.write %(#{id} [shape=box label="#{label}"]\n)
17
19
  end
20
+
18
21
  def id
19
22
  sentence.id
20
23
  end
24
+
21
25
  def label
22
26
  sentence.value
23
27
  end
28
+
24
29
  def ends
25
30
  [id]
26
31
  end
@@ -28,33 +33,34 @@ module Markdoc
28
33
 
29
34
  class IfLiteral < Treetop::Runtime::SyntaxNode
30
35
  def out(file)
31
- file.write %Q(#{id} [shape=diamond label="#{cond.value}"]\n)
36
+ file.write %(#{id} [shape=diamond label="#{cond.value}"]\n)
32
37
 
33
38
  unless yes.nil?
34
39
  yes.out(file)
35
- file.write %Q( #{id} -> #{yes.id} [label="Yes"]\n)
40
+ file.write %( #{id} -> #{yes.id} [label="Yes"]\n)
36
41
  end
37
42
  unless no.nil?
38
43
  no.out(file)
39
- file.write %Q( #{id} -> #{no.id} [label="No"]\n)
44
+ file.write %( #{id} -> #{no.id} [label="No"]\n)
40
45
  end
41
46
  end
42
47
 
43
48
  def id
44
49
  cond.id
45
50
  end
51
+
46
52
  def ends
47
53
  ary = []
48
- if yes.elements.empty?
49
- ary << yes.id
50
- else
51
- ary << yes.elements.last.ends
52
- end
53
- if no.elements.empty?
54
- ary << no.id
55
- else
56
- ary << no.elements.last.ends
57
- end
54
+ ary << if yes.elements.empty?
55
+ yes.id
56
+ else
57
+ yes.elements.last.ends
58
+ end
59
+ ary << if no.elements.empty?
60
+ no.id
61
+ else
62
+ no.elements.last.ends
63
+ end
58
64
  ary.flatten
59
65
  end
60
66
  end
@@ -63,6 +69,7 @@ module Markdoc
63
69
  def value
64
70
  text_value.strip
65
71
  end
72
+
66
73
  def id
67
74
  @id ||= Register.id
68
75
  end
@@ -73,11 +80,10 @@ module Markdoc
73
80
  prev = nil
74
81
  elements.each do |node|
75
82
  next if node.nil?
83
+
76
84
  node.out(file)
77
- unless prev.nil?
78
- prev.ends.each do |endid|
79
- file.write %Q(#{endid} -> #{node.id}\n)
80
- end
85
+ prev&.ends&.each do |endid|
86
+ file.write %(#{endid} -> #{node.id}\n)
81
87
  end
82
88
  prev = node
83
89
  end
@@ -90,11 +96,11 @@ module Markdoc
90
96
  def ends
91
97
  ary = []
92
98
  elements.each do |node|
93
- if node.elements.empty?
94
- ary << node.id
95
- else
96
- ary << node.ends
97
- end
99
+ ary << if node.elements.empty?
100
+ node.id
101
+ else
102
+ node.ends
103
+ end
98
104
  end
99
105
  ary.flatten
100
106
  end
@@ -104,7 +110,7 @@ module Markdoc
104
110
  parser = PseudocodeParser.new
105
111
  tree = parser.parse(code)
106
112
 
107
- if(tree.nil?)
113
+ if tree.nil?
108
114
  puts parser.failure_reason
109
115
  raise "Can't generate graphviz code"
110
116
  else
@@ -118,9 +124,7 @@ module Markdoc
118
124
  graphviz = file.path
119
125
  end
120
126
 
121
- if format == :graphviz
122
- return IO.read(graphviz)
123
- end
127
+ return IO.read(graphviz) if format == :graphviz
124
128
 
125
129
  image = Tempfile.new([digest, ".#{format}"])
126
130
  image.close
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'redcarpet'
2
4
  require 'pygments'
3
5
 
4
6
  module Markdoc
5
7
  class Renderer < Redcarpet::Render::HTML
6
-
7
8
  def block_code(code, language)
8
9
  case language
9
10
  when 'pseudo', 'pseudocode'
@@ -17,27 +18,27 @@ module Markdoc
17
18
 
18
19
  # removes xml or doctype meta info
19
20
  def wrap_svg(source)
20
- stripped = source.
21
- sub(/<\?xml[^>]+>/i, '').
22
- sub(/<!DOCTYPE[^>]+>/im, '').
23
- gsub(/<!\-\-[^>]+\-\->/, '')
21
+ stripped = source
22
+ .sub(/<\?xml[^>]+>/i, '')
23
+ .sub(/<!DOCTYPE[^>]+>/im, '')
24
+ .gsub(/<!--[^>]+-->/, '')
24
25
 
25
- %Q(<div class="svg-holder">\n#{stripped}\n</div>)
26
+ %(<div class="svg-holder">\n#{stripped}\n</div>)
26
27
  end
27
28
 
28
29
  def doc_header
29
- <<-END
30
- <html>
31
- <head>
32
- <title>Doc</title>
33
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
34
- <style>
35
- #{IO.read File.expand_path('../../../css/style.css', __FILE__)}
36
- #{IO.read File.expand_path('../../../css/pygments.css', __FILE__)}
37
- </style>
38
- </head>
39
- <body>
40
- END
30
+ <<~HEADER
31
+ <html>
32
+ <head>
33
+ <title>Doc</title>
34
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
35
+ <style>
36
+ #{IO.read File.expand_path('../../css/style.css', __dir__)}
37
+ #{IO.read File.expand_path('../../css/pygments.css', __dir__)}
38
+ </style>
39
+ </head>
40
+ <body>
41
+ HEADER
41
42
  end
42
43
 
43
44
  def doc_footer
@@ -1,210 +1,319 @@
1
+ # frozen_string_literal: false
2
+
1
3
  module Markdoc
2
4
  module Sequence
3
- def self.draw(code, format = :svg)
4
- parser = Parser.new(code)
5
+ DEFAULTS = {
6
+ diagram: {
7
+ offsetx: 10,
8
+ offsety: 10,
9
+ width: 900,
10
+ height: 600
11
+ },
12
+ role: {
13
+ font: "'Roboto Condensed', sans-serif",
14
+ border: '#3c4260',
15
+ fill: '#dcd7d7',
16
+ radius: 2,
17
+ spacing: 100,
18
+ width: 100,
19
+ height: 55,
20
+ line: 3
21
+ },
22
+ message: {
23
+ color: '#3c4260',
24
+ font: "'Roboto Condensed', sans-serif",
25
+ size: 11,
26
+ spacing: 40,
27
+ offset: 100, # from top
28
+ line: 3,
29
+ dash: '4,2'
30
+ }
31
+ }.freeze
32
+
33
+ def self.draw(code)
34
+ diagram = Diagram.new(code)
35
+ diagram.parse
36
+ diagram.print
37
+ end
5
38
 
6
- digest = Digest::MD5.hexdigest code
39
+ class Role
40
+ include Comparable
41
+
42
+ attr_accessor :id, :label, :messages, :column,
43
+ :offsetx, :offsety, :border, :fill, :radius, :spacing, :width, :height, :line, :font,
44
+ :prev, :succ
45
+
46
+ def initialize(args)
47
+ self.messages = []
48
+ self.id = args[:id].strip
49
+ self.label = args[:label].strip
7
50
 
8
- pic = nil
9
- Tempfile.open([digest, '.pic']) do |file|
10
- file.write parser.parse
11
- pic = file.path
51
+ # ui settings
52
+ self.column = args[:column]
53
+ self.offsetx = args[:diagram][:offsetx]
54
+ self.offsety = args[:diagram][:offsety]
55
+ self.border = args[:ui][:border]
56
+ self.fill = args[:ui][:fill]
57
+ self.radius = args[:ui][:radius]
58
+ self.spacing = args[:ui][:spacing]
59
+ self.width = args[:ui][:width]
60
+ self.height = args[:ui][:height]
61
+ self.line = args[:ui][:line]
62
+ self.font = args[:ui][:font]
12
63
  end
13
64
 
14
- if format == :pic
15
- return IO.read(pic)
65
+ def <=>(other)
66
+ column <=> other.column
16
67
  end
17
68
 
18
- image = Tempfile.new([digest, ".#{format}"])
19
- image.close
69
+ def type
70
+ case label
71
+ when /actor/i
72
+ :actor
73
+ when /database/i
74
+ :database
75
+ when /site|web/i
76
+ :website
77
+ when /application|system/i
78
+ :system
79
+ else
80
+ :component
81
+ end
82
+ end
20
83
 
21
- if system("pic2plot -T#{format} #{pic} > #{image.path}")
22
- IO.read image
23
- else
24
- raise "Can't generate sequence diagram"
84
+ def center
85
+ x + width / 2
25
86
  end
26
- end
27
87
 
28
- class Role
29
- attr_accessor :type, :id, :label, :active
30
- def initialize(type, id, label)
31
- self.type, self.id, self.label = type, id, label
32
- self.active = false
88
+ def x
89
+ offsetx + column * (width + spacing)
33
90
  end
34
- def activate
35
- return if active
36
- self.active = true
37
- "active(#{id});"
91
+
92
+ def y
93
+ offsety
38
94
  end
39
- def deactivate
40
- return unless active
41
- self.active = false
42
- "inactive(#{id});"
95
+
96
+ def print
97
+ elements = []
98
+ case type
99
+ when :actor
100
+ elements << %[<g transform="translate(#{x + 10},0)"><path d="M74,64 a30,30 0 0,0 -27,-27 a16,18 0 1,0 -16,0 a30,30 0, 0,0 -27,27 z" stroke-width="#{line}" fill="#{fill}" stroke="#{border}"/></g>]
101
+ elements << %(<text x="#{x + 46 - 2 * id.size}" y="#{y + height - 5}" font-family="#{font}" font-size="12" fill="#{border}">#{id}</text>)
102
+ else
103
+ elements << %(<rect fill="#{fill}" stroke="#{border}" rx="#{radius}" ry="#{radius}" x="#{x}" y="#{y}" width="#{width}" height="#{height}" stroke-width="#{line}"/>)
104
+ elements << %(<text x="#{x + 10}" y="#{y + 20}" font-family="#{font}" font-size="12" fill="#{border}">#{label}</text>)
105
+ end
106
+
107
+ x1 = center
108
+ y1 = offsety + height
109
+ x2 = center
110
+ y2 = messages.last.y + 10
111
+ elements << %(<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{border}" stroke-width="#{line}"/>)
112
+
113
+ elements.join("\n")
43
114
  end
44
115
  end
45
116
 
46
117
  class Message
47
- attr_accessor :type, :source, :dest, :label, :comment
48
- def initialize(type, source, dest, label, comment)
49
- self.type, self.source, self.dest, self.label, self.comment = type, source, dest, label.strip, comment.strip
50
- end
118
+ attr_accessor :source, :dest, :op, :label, :comment, :row,
119
+ :offset, :color, :font, :size, :spacing, :line, :dash,
120
+ :options
121
+
122
+ def initialize(args)
123
+ self.options = []
124
+ self.label = args[:label].strip
125
+ self.comment = args[:comment].strip
126
+
127
+ self.op = args[:op]
128
+ self.row = args[:row]
129
+ # ui
130
+ self.offset = args[:ui][:offset]
131
+ self.color = args[:ui][:color]
132
+ self.font = args[:ui][:font]
133
+ self.size = args[:ui][:size]
134
+ self.spacing = args[:ui][:spacing]
135
+ self.line = args[:ui][:line]
136
+ self.dash = args[:ui][:dash]
137
+
138
+ if op.index('~')
139
+ options << %(stroke-dasharray="#{dash}")
140
+ elsif op.index('-').nil?
141
+ raise 'Message direction must be one of ->, ~>, <-, <~'
142
+ end
51
143
 
52
- def roles
53
- [source, dest]
144
+ if op.index('>')
145
+ self.source = args[:role1]
146
+ self.dest = args[:role2]
147
+ elsif op.index('<')
148
+ self.source = args[:role2]
149
+ self.dest = args[:role1]
150
+ else
151
+ raise 'Message direction must be one of ->, ~>, <-, <~'
152
+ end
153
+
154
+ source.messages << self
155
+ dest.messages << self unless source.eql?(dest)
54
156
  end
55
157
 
56
- def deliver
57
- %Q[#{type}(#{source.id},#{dest.id}, "#{label}");]
158
+ def print
159
+ role1, role2 = *(source < dest ? [source, dest] : [dest, source])
160
+ elements = []
161
+
162
+ x1 = role1.center
163
+ if role1.eql?(role2)
164
+ y1 = y
165
+ x2 = x1 + 50
166
+ y2 = y1 + spacing
167
+
168
+ elements << %(<polyline points="#{x1},#{y1} #{x2},#{y1} #{x2},#{y2} #{x1 + 5},#{y2}" fill="none" stroke-width="2" stroke-linejoin="round" stroke="#{color}" #{options.join ' '}/>)
169
+ elements << %(<polygon points="#{x1 + 10},#{y2 - 5} #{x1},#{y2} #{x1 + 10},#{y2 + 5}" fill="#{color}"/>)
170
+ elements << %(<text x="#{x1 + 10}" y="#{y1 - 5}" font-family="#{font}" font-size="#{size}" fill="#{color}">#{label}</text>)
171
+ else
172
+ x2 = role2.center
173
+
174
+ if role1 == source
175
+ x2 -= 10
176
+ elements << %(<polygon points="#{x2},#{y - 5} #{x2 + 10},#{y} #{x2},#{y + 5}" fill="#{color}"/>)
177
+ else
178
+ x1 += 10
179
+ elements << %(<polygon points="#{x1},#{y - 5} #{x1 - 10},#{y} #{x1},#{y + 5}" fill="#{color}"/>)
180
+ end
181
+
182
+ elements << %(<line x1="#{x1}" y1="#{y}" x2="#{x2}" y2="#{y}" stroke="#{color}" stroke-width="2" #{options.join ' '}/>)
183
+ elements << %(<text x="#{x1 + 40}" y="#{y - 5}" font-family="#{font}" font-size="#{size}" fill="#{color}">#{label}</text>)
184
+
185
+ if comment.size.positive?
186
+ x = role2.prev.center + 15
187
+ elements << %(<path fill="#eeeeee" d="M#{x2 - 30},#{y + 1} L#{x2 - 30},#{y + 10} H#{x} V#{y + 2 * spacing - 25} H#{x2} V#{y + 10} H#{x2 - 20} z" />)
188
+ elements << %(<text x="#{x + 5}" y="#{y + 23}" font-family="#{font}" font-size="#{size}" fill="#{color}">)
189
+ split(comment).each_with_index do |line, i|
190
+ elements << %(<tspan x="#{x + 5}" y="#{y + 23 + 13 * i}">#{line}</tspan>)
191
+ end
192
+ elements << '</text>'
193
+ end
194
+ end
195
+
196
+ elements.join("\n")
58
197
  end
59
198
 
60
- def describe
61
- return if comment.empty?
62
- width = comment.length > 10 ? 1 : comment.length * 0.13
63
- width = 0.5 if width < 0.5
64
- %Q[comment(#{dest.id},C,up 0.2 right, wid #{'%.1f' % width} ht 0.3 "#{comment}");]
199
+ def y
200
+ offset + row * spacing
65
201
  end
66
202
 
67
- def height
68
- source == dest ? 2 : 1
203
+ private
204
+
205
+ def split(text, max = 35)
206
+ ary = []
207
+ line = ''
208
+ text.split.each do |word|
209
+ if (line + word).length < max
210
+ line << ' ' << word
211
+ else
212
+ ary << line
213
+ line = word
214
+ end
215
+ end
216
+ ary << line if line.length.positive?
217
+ ary
69
218
  end
70
219
  end
71
220
 
72
- class Parser
73
- attr_accessor :source, :variables, :roles, :messages, :output
74
-
75
- def defaults
76
- {
77
- boxht: '0.5', # Object box height
78
- boxwid: '1.3', # Object box width
79
- awid: '0.1', # Active lifeline width
80
- spacing: '0.25', # Spacing between messages
81
- movewid: '0.75', # Spacing between objects
82
- dashwid: '0.05', # Interval for dashed lines
83
- maxpswid: '20', # Maximum width of picture
84
- maxpsht: '20', # Maximum height of picture
85
- underline: '0', # Underline the name of objects
86
- }
87
- end
88
-
89
- def initialize(source, options = {})
90
- self.source = source
221
+ class Diagram
222
+ attr_accessor :input, :output, :attributes, :roles, :messages, :rows
223
+
224
+ def initialize(input, options = {})
225
+ self.input = input
91
226
  self.output = []
92
227
  self.roles = []
93
228
  self.messages = []
94
- self.variables = defaults.dup
229
+ self.rows = 0
230
+ self.attributes = DEFAULTS.dup
231
+
95
232
  options.each do |key, value|
96
- variables[key] = value
233
+ attributes.merge!(key => value)
97
234
  end
98
235
  end
99
236
 
100
237
  def find(id)
101
- id.strip!
102
- roles.detect{|role| role.id == id}
238
+ roles.detect { |role| role.id == id.strip } or raise("Non-declared role: #{id}")
103
239
  end
104
240
 
105
241
  def parse
106
- source.split("\n").each do |line|
242
+ input.split("\n").each do |line|
107
243
  next if line.strip.empty?
108
- if match = line.match(/^([a-zA-Z0-9_ \t]+) *= *([a-zA-Z0-9_ \t]+)/)
109
- if match[2] =~ /Actor/
110
- roles << Role.new(:actor, match[1].strip, match[1].strip)
111
- else
112
- roles << Role.new(:object, match[1].strip, match[2].strip)
113
- end
114
- elsif match = line.match(/^([a-zA-Z0-9_ \t]+) *([<\-~>]{2}) *([a-zA-Z0-9_ \t]+):([^#]+)#?(.*)/)
115
- role1, role2, op = find(match[1]), find(match[3]), match[2]
116
-
117
- if op.index('>')
118
- source, dest = role1, role2
119
- elsif op.index('<')
120
- source, dest = role2, role1
121
- else
122
- raise "Message direction must be one of ->, ~>, <-, <~"
123
- end
124
244
 
125
- if op.index('-')
126
- type = :message
127
- elsif op.index('~')
128
- type = :rmessage
129
- else
130
- raise "Message direction must be one of ->, ~>, <-, <~"
131
- end
132
-
133
- message = Message.new(type, source, dest, match[4], match[5])
134
- # deactivate prev messages roles
135
- if last = messages.last
136
- (last.roles - message.roles).each do |role|
137
- output << role.deactivate if role.active
138
- end
139
- end
140
- # activate source before send message
141
- output << source.activate unless source.active
142
- output << message.deliver
143
- output << message.describe
144
- # activate dest
145
- output << dest.activate unless dest.active
146
- messages << message
147
- elsif match = line.match(/^([a-zA-Z0-9_ \t]+) *:([^#]+)#?(.*)/)
148
- role = find(match[1])
149
- message = Message.new(:message, role, role, match[2], match[3])
150
- # deactivate prev messages roles
151
- if last = messages.last
152
- (last.roles - message.roles).each do |role|
153
- output << role.deactivate if role.active
154
- end
155
- end
156
- # activate role before send message
157
- output << role.activate unless role.active
158
- output << message.deliver
159
- output << message.describe
160
- messages << message
245
+ if (matches = line.match(/^(?<id>[a-zA-Z0-9_ \t]+) *= *(?<label>[a-zA-Z0-9_ \t]+)/))
246
+ # User = Actor
247
+ roles << Role.new(
248
+ id: matches[:id],
249
+ label: matches[:label],
250
+ column: roles.size,
251
+ diagram: attributes[:diagram],
252
+ ui: attributes[:role]
253
+ )
254
+ elsif (matches = line.match(/^(?<role1>[a-zA-Z0-9_ \t]+) *(?<op>[<\-~>]{2}) *(?<role2>[a-zA-Z0-9_ \t]+):(?<label>[^#]+)#?(?<comment>.*)/))
255
+ # User -> Web : Login
256
+ messages << Message.new(
257
+ role1: find(matches[:role1]),
258
+ role2: find(matches[:role2]),
259
+ row: rows,
260
+ op: matches[:op],
261
+ label: matches[:label],
262
+ comment: matches[:comment],
263
+ diagram: attributes[:diagram],
264
+ ui: attributes[:message]
265
+ )
266
+ self.rows += 1
267
+ # comment takes 1 more row
268
+ self.rows += 1 if matches[:comment].length.positive?
269
+ elsif (matches = line.match(/^(?<role>[a-zA-Z0-9_ \t]+) *:(?<label>[^#]+)#?(?<comment>.*)/))
270
+ # Web : Save the form
271
+ messages << Message.new(
272
+ role1: find(matches[:role]),
273
+ role2: find(matches[:role]),
274
+ row: rows,
275
+ op: '->',
276
+ label: matches[:label],
277
+ comment: matches[:comment],
278
+ diagram: attributes[:diagram],
279
+ ui: attributes[:message]
280
+ )
281
+ # self message takes 2 rows
282
+ self.rows += 2
161
283
  end
162
284
  end
163
285
 
164
- header
165
- footer
166
-
167
- output.compact.join("\n")
286
+ prev = nil
287
+ roles.each do |succ|
288
+ succ.prev = prev
289
+ prev.succ = succ if prev
290
+ prev = succ
291
+ end
168
292
  end
169
293
 
170
- def macros
171
- IO.read File.join(File.dirname(__FILE__), 'sequence.pic')
294
+ def print
295
+ format(template, width: width, height: height, content: (roles + messages).map(&:print).join("\n"))
172
296
  end
173
297
 
174
- def header
175
- headers = []
176
- headers << '.PS'
177
- headers << ''
178
- headers << macros
179
- headers << ''
180
- headers << '# Variables'
181
- # calculate height
182
-
183
- variables[:maxpsht] = ((variables[:spacing].to_f *
184
- messages.map(&:height).reduce(:+)) +
185
- variables[:boxht].to_f).ceil
298
+ def height
299
+ attributes[:message][:offset] +
300
+ attributes[:message][:spacing] * rows +
301
+ attributes[:diagram][:offsety] * 2
302
+ end
186
303
 
187
- variables.each do |key, value|
188
- headers << "#{key} = #{value};"
189
- end
190
- headers << '# Actors and Objects'
191
- roles.each do |object|
192
- headers << %Q[#{object.type}(#{object.id},"#{object.label}");]
193
- end
194
- headers << 'step();'
195
- headers << ''
196
- self.output = headers + output
304
+ def width
305
+ (attributes[:role][:spacing] + attributes[:role][:width]) * roles.size +
306
+ attributes[:diagram][:offsetx]
197
307
  end
198
308
 
199
- def footer
200
- output << ''
201
- output << 'step();'
202
- output << '# Complete the lifelines'
203
- roles.each do |object|
204
- output << "complete(#{object.id});"
205
- end
206
- output << ''
207
- output << '.PE'
309
+ private
310
+
311
+ def template
312
+ '<svg xmlns="http://www.w3.org/2000/svg" ' \
313
+ 'xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" ' \
314
+ 'width="%<width>s" height="%<height>s" ' \
315
+ 'viewBox="0 0 %<width>s %<height>s">' \
316
+ '%<content>s</svg>'
208
317
  end
209
318
  end
210
319
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Markdoc
2
- VERSION = '1.1.0'
4
+ VERSION = '2.0.0'
3
5
  end
data/lib/markdoc.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
  require 'tempfile'
3
5
 
metadata CHANGED
@@ -1,80 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 2.0.0
6
5
  platform: ruby
7
6
  authors:
8
- - Lkhagva Ochirkhuyag
9
- autorequire:
7
+ - Ochirkhuyag.L
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-04-10 00:00:00.000000000 Z
11
+ date: 2022-06-15 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: polyglot
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0.3'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pygments.rb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: redcarpet
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ~>
45
+ - - "~>"
36
46
  - !ruby/object:Gem::Version
37
- version: '3.2'
47
+ version: '3.5'
38
48
  type: :runtime
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ~>
52
+ - - "~>"
44
53
  - !ruby/object:Gem::Version
45
- version: '3.2'
54
+ version: '3.5'
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: treetop
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ~>
59
+ - - "~>"
52
60
  - !ruby/object:Gem::Version
53
61
  version: '1.6'
54
62
  type: :runtime
55
63
  prerelease: false
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
65
  requirements:
59
- - - ~>
66
+ - - "~>"
60
67
  - !ruby/object:Gem::Version
61
68
  version: '1.6'
62
69
  - !ruby/object:Gem::Dependency
63
- name: pygments.rb
70
+ name: rake
64
71
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
72
  requirements:
67
- - - ~>
73
+ - - "~>"
68
74
  - !ruby/object:Gem::Version
69
- version: '0.6'
70
- type: :runtime
75
+ version: '13.0'
76
+ type: :development
71
77
  prerelease: false
72
78
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
79
  requirements:
75
- - - ~>
80
+ - - "~>"
76
81
  - !ruby/object:Gem::Version
77
- version: '0.6'
82
+ version: '13.0'
78
83
  description: Markdown with support for pseudocode to flowchart and sequence diagram
79
84
  generation
80
85
  email: ochkoo@gmail.com
@@ -85,41 +90,38 @@ executables:
85
90
  extensions: []
86
91
  extra_rdoc_files: []
87
92
  files:
93
+ - bin/markdoc
94
+ - bin/pseudo2svg
95
+ - bin/sequence2svg
96
+ - css/pygments.css
97
+ - css/style.css
88
98
  - lib/markdoc.rb
89
99
  - lib/markdoc/pseudocode.rb
90
100
  - lib/markdoc/pseudocode.treetop
91
101
  - lib/markdoc/renderer.rb
92
102
  - lib/markdoc/sequence.rb
93
- - lib/markdoc/sequence.pic
94
103
  - lib/markdoc/version.rb
95
- - css/style.css
96
- - css/pygments.css
97
- - bin/markdoc
98
- - bin/pseudo2svg
99
- - bin/sequence2svg
100
104
  homepage: https://github.com/ochko/markdoc
101
105
  licenses:
102
106
  - MIT
103
- post_install_message:
107
+ metadata: {}
108
+ post_install_message:
104
109
  rdoc_options: []
105
110
  require_paths:
106
111
  - lib
107
112
  required_ruby_version: !ruby/object:Gem::Requirement
108
- none: false
109
113
  requirements:
110
- - - ! '>='
114
+ - - ">="
111
115
  - !ruby/object:Gem::Version
112
- version: 1.9.2
116
+ version: 2.7.6
113
117
  required_rubygems_version: !ruby/object:Gem::Requirement
114
- none: false
115
118
  requirements:
116
- - - ! '>='
119
+ - - ">="
117
120
  - !ruby/object:Gem::Version
118
121
  version: '0'
119
122
  requirements: []
120
- rubyforge_project:
121
- rubygems_version: 1.8.25
122
- signing_key:
123
- specification_version: 3
123
+ rubygems_version: 3.1.6
124
+ signing_key:
125
+ specification_version: 4
124
126
  summary: Markdown to HTML converter with diagrams
125
127
  test_files: []
@@ -1,406 +0,0 @@
1
- #/usr/bin/pic2plot -Tps
2
- #
3
- # Pic macros for drawing UML sequence diagrams
4
- #
5
- # (C) Copyright 2004-2005 Diomidis Spinellis.
6
- #
7
- # Permission to use, copy, and distribute this software and its
8
- # documentation for any purpose and without fee is hereby granted,
9
- # provided that the above copyright notice appear in all copies and that
10
- # both that copyright notice and this permission notice appear in
11
- # supporting documentation.
12
- #
13
- # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
14
- # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
15
- # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16
- #
17
- #
18
-
19
-
20
- # Default parameters (can be redefined)
21
-
22
- # Spacing between messages
23
- spacing = 0.25;
24
- # Active box width
25
- awid = .1;
26
- # Box height
27
- boxht = 0.3;
28
- # Commend folding
29
- corner_fold=awid
30
- # Comment distance
31
- define comment_default_move {up 0.25 right 0.25};
32
- # Comment height
33
- comment_default_ht=0.5;
34
- # Comment width
35
- comment_default_wid=1;
36
- # Underline object name
37
- underline=1;
38
-
39
- # Create a new object(name,label)
40
- define object {
41
- $1: box $2; move;
42
- # Could also underline text with \mk\ul\ul\ul...\rt
43
- if (underline) then {
44
- line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
45
- }
46
- move to $1.e;
47
- move right;
48
- # Active is the level of activations of the object
49
- # 0 : inactive : draw thin line swimlane
50
- # 1 : active : draw thick swimlane
51
- # > 1: nested : draw nested swimlane
52
- active_$1 = 0;
53
- lifestart_$1 = $1.s.y;
54
- }
55
-
56
- # Create a new external actor(name,label)
57
- define actor {
58
- $1: [
59
- XSEQC: circle rad 0.06;
60
- XSEQL: line from XSEQC.s down .12;
61
- line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
62
- XSEQL1: line from XSEQL.end left .08 down .15;
63
- XSEQL2: line from XSEQL.end right .08 down .15;
64
- line at XSEQC.n invis "" "" "" $2;
65
- ]
66
- move to $1.e;
67
- move right;
68
- active_$1 = 0;
69
- lifestart_$1 = $1.s.y - .05;
70
- }
71
-
72
- # Create a new placeholder object(name)
73
- define placeholder_object {
74
- $1: box invisible;
75
- move;
76
- move to $1.e;
77
- move right;
78
- active_$1 = 0;
79
- lifestart_$1 = $1.s.y;
80
- }
81
-
82
- define pobject {
83
- placeholder_object($1);
84
- }
85
-
86
- define extend_lifeline {
87
- if (active_$1 > 0) then {
88
- # draw the left edges of the boxes
89
- move to ($1.x - awid/2, Here.y);
90
- for level = 1 to active_$1 do {
91
- line from (Here.x, lifestart_$1) to Here;
92
- move right awid/2
93
- }
94
-
95
- # draw the right edge of the innermost box
96
- move right awid/2;
97
- line from (Here.x, lifestart_$1) to Here;
98
- } else {
99
- line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
100
- }
101
- lifestart_$1 = Here.y;
102
- }
103
-
104
- # complete(name)
105
- # Complete the lifeline of the object with the given name
106
- define complete {
107
- extend_lifeline($1)
108
- if (active_$1) then {
109
- # draw bottom of all active boxes
110
- line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
111
- }
112
- }
113
-
114
- # Draw a message(from_object,to_object,label)
115
- define message {
116
- down;
117
- move spacing;
118
- # Adjust so that lines and arrows do not fall into the
119
- # active box. Should be .5, but the arrow heads tend to
120
- # overshoot.
121
- if ($1.x <= $2.x) then {
122
- off_from = awid * .6;
123
- off_to = -awid * .6;
124
- } else {
125
- off_from = -awid * .6;
126
- off_to = awid * .6;
127
- }
128
-
129
- # add half a box width for each level of nesting
130
- if (active_$1 > 1) then {
131
- off_from = off_from + (active_$1 - 1) * awid/2;
132
- }
133
-
134
- # add half a box width for each level of nesting
135
- if (active_$2 > 1) then {
136
- off_to = off_to + (active_$2 - 1) * awid/2;
137
- }
138
-
139
- if ($1.x == $2.x) then {
140
- arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
141
- } else {
142
- arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
143
- }
144
- }
145
-
146
- # Display a lifeline constraint(object,label)
147
- define lifeline_constraint {
148
- off_from = awid;
149
- # add half a box width for each level of nesting
150
- if (active_$1 > 1) then {
151
- off_from = off_from + (active_$1 - 1) * awid/2;
152
- }
153
-
154
- box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
155
- }
156
-
157
- define lconstraint {
158
- lifeline_constraint($1,$2);
159
- }
160
-
161
- # Display an object constraint(label)
162
- # for the last object drawn
163
- define object_constraint {
164
- { box invis with .s at last box .nw $1 ljust; }
165
- }
166
-
167
- define oconstraint {
168
- object_constraint($1);
169
- }
170
-
171
- # Draw a creation message(from_object,to_object,object_label)
172
- define create_message {
173
- down;
174
- move spacing;
175
- if ($1.x <= $2.x) then {
176
- off_from = awid * .6;
177
- off_to = -boxwid * .51;
178
- } else {
179
- off_from = -awid * .6;
180
- off_to = boxwid * .51;
181
- }
182
-
183
- # add half a box width for each level of nesting
184
- if (active_$1 > 1) then {
185
- off_from = off_from + (active_$1 - 1) * awid/2;
186
- }
187
-
188
- # See comment in destroy_message
189
- XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "�create�" " ";
190
- if ($1.x <= $2.x) then {
191
- { XSEQB: box $3 with .w at XSEQA.end; }
192
- } else {
193
- { XSEQB: box $3 with .e at XSEQA.end; }
194
- }
195
- {
196
- line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
197
- }
198
- lifestart_$2 = XSEQB.s.y;
199
- move (spacing + boxht) / 2;
200
- }
201
-
202
- define cmessage {
203
- create_message($1,$2,$3);
204
- }
205
-
206
- # Draw an X for a given object
207
- define drawx {
208
- {
209
- line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
210
- line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
211
- }
212
- }
213
-
214
- # Draw a destroy message(from_object,to_object)
215
- define destroy_message {
216
- down;
217
- move spacing;
218
- # The troff code is \(Fo \(Fc
219
- # The groff code is also \[Fo] \[Fc]
220
- # The pic2plot code is \Fo \Fc
221
- # See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
222
- # To stay compatible with all we have to hardcode the characters
223
- message($1,$2,"�destroy�");
224
- complete($2);
225
- drawx($2);
226
- }
227
-
228
- define dmessage {
229
- destroy_message($1,$2);
230
- }
231
-
232
- # An object deletes itself: delete(object)
233
- define delete {
234
- complete($1);
235
- lifestart_$1 = lifestart_$1 - awid;
236
- drawx($1);
237
- }
238
-
239
- # Draw a message return(from_object,to_object,label)
240
- define return_message {
241
- down;
242
- move spacing;
243
- # See comment in message
244
- if ($1.x <= $2.x) then {
245
- off_from = awid * .6;
246
- off_to = -awid * .6;
247
- } else {
248
- off_from = -awid * .6;
249
- off_to = awid * .6;
250
- }
251
-
252
- # add half a box width for each level of nesting
253
- if (active_$1 > 1) then {
254
- off_from = off_from + (active_$1 - 1) * awid/2;
255
- }
256
-
257
- # add half a box width for each level of nesting
258
- if (active_$2 > 1) then {
259
- off_to = off_to + (active_$2 - 1) * awid/2;
260
- }
261
-
262
- arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
263
- }
264
-
265
- define rmessage {
266
- return_message($1,$2,$3);
267
- }
268
-
269
- # Object becomes active
270
- # Can be nested to show recursion
271
- define active {
272
- extend_lifeline($1);
273
- # draw top of new active box
274
- line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
275
- active_$1 = active_$1 + 1;
276
- }
277
-
278
- # Object becomes inactive
279
- # Can be nested to show recursion
280
- define inactive {
281
- extend_lifeline($1);
282
- active_$1 = active_$1 - 1;
283
- # draw bottom of innermost active box
284
- line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
285
- }
286
-
287
- # Time step
288
- # Useful at the beginning and the end
289
- # to show object states
290
- define step {
291
- down;
292
- move spacing;
293
- }
294
-
295
- # Switch to asynchronous messages
296
- define async {
297
- arrowhead = 0;
298
- arrowwid = arrowwid * 2;
299
- }
300
-
301
- # Switch to synchronous messages
302
- define sync {
303
- arrowhead = 1;
304
- arrowwid = arrowwid / 2;
305
- }
306
-
307
- # same as lifeline_constraint, but Text and empty string are exchanged.
308
- define lconstraint_below{
309
- off_from = awid;
310
- # add half a box width for each level of nesting
311
- if (active_$1 > 1) then {
312
- off_from = off_from + (active_$1 - 1) * awid/2;
313
- }
314
-
315
- box at ($1.x + off_from, Here.y) invis "" $2 ljust;
316
- }
317
-
318
- # begin_frame(left_object,name,label_text);
319
- define begin_frame {
320
- # The lifeline will be cut here
321
- extend_lifeline($1);
322
- # draw the frame-label
323
- $2: box $3 invis with .n at ($1.x, Here.y);
324
- d = $2.e.y - $2.se.y;
325
- line from $2.ne to $2.e then down d left d then to $2.sw;
326
- # continue the lifeline below the frame-label
327
- move to $2.s;
328
- lifestart_$1 = Here.y;
329
- }
330
-
331
- # end_frame(right_object,name);
332
- define end_frame {
333
- # dummy-box for the lower right corner:
334
- box invis "" with .s at ($1.x, Here.y);
335
- # draw the frame
336
- frame_wid = last box.se.x - $2.nw.x
337
- frame_ht = - last box.se.y + $2.nw.y
338
- box with .nw at $2.nw wid frame_wid ht frame_ht;
339
- # restore Here.y
340
- move to last box.s;
341
- }
342
-
343
- # comment(object,[name],[line_movement], [box_size] text);
344
- define comment {
345
- old_y = Here.y
346
- # draw the first connecting line, at which's end the box wil be positioned
347
- move to ($1.x, Here.y)
348
- if "$3" == "" then {
349
- line comment_default_move() dashed;
350
- } else {
351
- line $3 dashed;
352
- }
353
-
354
- # draw the box, use comment_default_xx if no explicit
355
- # size is given together with the text in parameter 4
356
- old_boxht=boxht;
357
- old_boxwid=boxwid;
358
- boxht=comment_default_ht;
359
- boxwid=comment_default_wid;
360
- if "$2" == "" then {
361
- box invis $4;
362
- } else {
363
- $2: box invis $4;
364
- }
365
- boxht=old_boxht;
366
- boxwid=old_boxwid;
367
-
368
- # draw the frame of the comment
369
- line from last box.nw \
370
- to last box.ne - (corner_fold, 0) \
371
- then to last box.ne - (0, corner_fold) \
372
- then to last box.se \
373
- then to last box.sw \
374
- then to last box.nw ;
375
- line from last box.ne - (corner_fold, 0) \
376
- to last box.ne - (corner_fold, corner_fold) \
377
- then to last box.ne - (0, corner_fold) ;
378
-
379
- # restore Here.y
380
- move to ($1.x, old_y)
381
- }
382
-
383
- # connect_to_comment(object,name);
384
- define connect_to_comment {
385
- old_y = Here.y
386
- # start at the object
387
- move to ($1.x, Here.y)
388
- # find the best connection-point of the comment to use as line-end
389
- if $1.x < $2.w.x then {
390
- line to $2.w dashed;
391
- } else {
392
- if $1.x > $2.e.x then {
393
- line to $2.e dashed;
394
- } else {
395
- if Here.y < $2.s.y then {
396
- line to $2.s dashed;
397
- } else {
398
- if Here.y > $2.n.y then {
399
- line to $2.n dashed;
400
- }
401
- }
402
- }
403
- }
404
- # restore Here.y
405
- move to ($1.x, old_y)
406
- }