markdoc 1.1.0 → 2.0.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 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
- }