railroad_diagrams 0.2.0 → 0.3.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.
@@ -117,24 +117,43 @@ module RailroadDiagrams
117
117
  end
118
118
  end
119
119
 
120
- def rect(item, dashed = false)
120
+ def rect(item, dashed: false)
121
121
  rectish('rect', item, dashed)
122
122
  end
123
123
 
124
- def round_rect(item, dashed = false)
124
+ def round_rect(item, dashed: false)
125
125
  rectish('roundrect', item, dashed)
126
126
  end
127
127
 
128
+ def max_width(*args)
129
+ max_width = 0
130
+ args.each do |arg|
131
+ width =
132
+ case arg
133
+ when TextDiagram
134
+ arg.width
135
+ when Array
136
+ arg.map(&:length).max
137
+ when Numeric
138
+ arg.to_s.length
139
+ else
140
+ arg.length
141
+ end
142
+ max_width = width if width > max_width
143
+ end
144
+ max_width
145
+ end
146
+
128
147
  def pad_l(string, width, pad)
129
148
  gap = width - string.length
130
- raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless gap % pad.length == 0
149
+ raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless (gap % pad.length).zero?
131
150
 
132
151
  (pad * (gap / pad.length)) + string
133
152
  end
134
153
 
135
154
  def pad_r(string, width, pad)
136
155
  gap = width - string.length
137
- raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless gap % pad.length == 0
156
+ raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless (gap % pad.length).zero?
138
157
 
139
158
  string + (pad * (gap / pad.length))
140
159
  end
@@ -169,54 +188,57 @@ module RailroadDiagrams
169
188
 
170
189
  def rectish(rect_type, data, dashed)
171
190
  line_type = dashed ? '_dashed' : ''
172
- parts = get_parts([
173
- "#{rect_type}_top_left",
174
- "#{rect_type}_left#{line_type}",
175
- "#{rect_type}_bot_left",
176
- "#{rect_type}_top_right",
177
- "#{rect_type}_right#{line_type}",
178
- "#{rect_type}_bot_right",
179
- "#{rect_type}_top#{line_type}",
180
- "#{rect_type}_bot#{line_type}",
181
- 'line',
182
- 'cross'
183
- ])
184
-
185
- item_td = data.is_a?(TextDiagram) ? data : new(0, 0, [data.to_s])
186
-
187
- lines = [parts[6] * (item_td.width + 2)]
188
- lines += item_td.expand(1, 1, 0, 0).lines.map { |line| " #{line} " }
189
- lines << (parts[7] * (item_td.width + 2))
191
+ top_left, ctr_left, bot_left, top_right, ctr_right, bot_right, top_horiz, bot_horiz, line, cross =
192
+ get_parts([
193
+ "#{rect_type}_top_left",
194
+ "#{rect_type}_left#{line_type}",
195
+ "#{rect_type}_bot_left",
196
+ "#{rect_type}_top_right",
197
+ "#{rect_type}_right#{line_type}",
198
+ "#{rect_type}_bot_right",
199
+ "#{rect_type}_top#{line_type}",
200
+ "#{rect_type}_bot#{line_type}",
201
+ 'line',
202
+ 'cross'
203
+ ])
204
+
205
+ item_td = data.is_a?(TextDiagram) ? data : new(0, 0, [data])
206
+
207
+ lines = [top_horiz * (item_td.width + 2)]
208
+ if data.is_a?(TextDiagram)
209
+ lines += item_td.expand(1, 1, 0, 0).lines
210
+ else
211
+ (0...item_td.lines.length).each do |i|
212
+ lines += [" #{item_td.lines[i]} "]
213
+ end
214
+ end
215
+ lines += [(bot_horiz * (item_td.width + 2))]
190
216
 
191
217
  entry = item_td.entry + 1
192
218
  exit = item_td.exit + 1
193
219
 
194
- left_max = [parts[0], parts[1], parts[2]].map(&:size).max
195
- lefts = Array.new(lines.size, parts[1].ljust(left_max))
196
- lefts[0] = parts[0].ljust(left_max, parts[6])
197
- lefts[-1] = parts[2].ljust(left_max, parts[7])
198
- lefts[entry] = parts[9].ljust(left_max) if data.is_a?(TextDiagram)
220
+ left_max_width = max_width(top_left, ctr_left, bot_left)
221
+ lefts = [pad_r(ctr_left, left_max_width, ' ')] * lines.length
222
+ lefts[0] = pad_r(top_left, left_max_width, top_horiz)
223
+ lefts[-1] = pad_r(bot_left, left_max_width, bot_horiz)
224
+ lefts[entry] = cross if data.is_a?(TextDiagram)
199
225
 
200
- right_max = [parts[3], parts[4], parts[5]].map(&:size).max
201
- rights = Array.new(lines.size, parts[4].rjust(right_max))
202
- rights[0] = parts[3].rjust(right_max, parts[6])
203
- rights[-1] = parts[5].rjust(right_max, parts[7])
204
- rights[exit] = parts[9].rjust(right_max) if data.is_a?(TextDiagram)
226
+ right_max_width = max_width(top_right, ctr_right, bot_right)
227
+ rights = [pad_l(ctr_right, right_max_width, ' ')] * lines.length
228
+ rights[0] = pad_l(top_right, right_max_width, top_horiz)
229
+ rights[-1] = pad_l(bot_right, right_max_width, bot_horiz)
230
+ rights[exit] = cross if data.is_a?(TextDiagram)
205
231
 
206
- new_lines = lines.each_with_index.map do |line, i|
207
- lefts[i] + line + rights[i]
208
- end
232
+ lines = enclose_lines(lines, lefts, rights)
209
233
 
210
- lefts = Array.new(lines.size, ' ')
211
- lefts[entry] = parts[8]
212
- rights = Array.new(lines.size, ' ')
213
- rights[exit] = parts[8]
234
+ lefts = [' '] * lines.length
235
+ lefts[entry] = line
236
+ rights = [' '] * lines.length
237
+ rights[exit] = line
214
238
 
215
- new_lines = new_lines.each_with_index.map do |line, i|
216
- lefts[i] + line + rights[i]
217
- end
239
+ lines = enclose_lines(lines, lefts, rights)
218
240
 
219
- new(entry, exit, new_lines)
241
+ new(entry, exit, lines)
220
242
  end
221
243
  end
222
244
 
@@ -227,12 +249,17 @@ module RailroadDiagrams
227
249
  @exit = exit
228
250
  @lines = lines.dup
229
251
  @height = lines.size
230
- @width = lines.empty? ? 0 : lines.first.size
252
+ @width = lines.any? ? lines[0].length : 0
253
+
254
+ raise "Entry is not within diagram vertically:\n#{dump(false)}" unless entry <= lines.length
255
+ raise "Exit is not within diagram vertically:\n#{dump(false)}" unless exit <= lines.length
231
256
 
232
- validate
257
+ lines.each do |line|
258
+ raise "Diagram data is not rectangular:\n#{dump(false)}" unless lines[0].length == line.length
259
+ end
233
260
  end
234
261
 
235
- def alter(new_entry = nil, new_exit = nil, new_lines = nil)
262
+ def alter(new_entry: nil, new_exit: nil, new_lines: nil)
236
263
  self.class.new(
237
264
  new_entry || @entry,
238
265
  new_exit || @exit,
@@ -310,23 +337,33 @@ module RailroadDiagrams
310
337
  )
311
338
  end
312
339
 
313
- private
314
-
315
- def validate
316
- return if @lines.empty?
317
-
318
- line_length = @lines.first.size
319
- @lines.each do |line|
320
- raise ArgumentError, "Diagram is not rectangular:\n#{inspect}" unless line.size == line_length
340
+ def dump(show = true)
341
+ result = "height=#{@height}; len(lines)=#{@lines.length}"
342
+
343
+ result += "; entry outside diagram: entry=#{@ntry}" if @entry > @lines.length
344
+ result += "; exit outside diagram: exit=#{@exit}" if @exit > @lines.length
345
+
346
+ (0...[@lines.length, @entry + 1, @exit + 1].max).each do |y|
347
+ result += "\n[#{format('%03d', y)}]"
348
+ result += " '#{@lines[y]}' len=#{@lines[y].length}" if y < @lines.length
349
+ if y == @entry && y == @exit
350
+ result += ' <- entry, exit'
351
+ elsif y == @entry
352
+ result += ' <- entry'
353
+ elsif y == @exit
354
+ result += ' <- exit'
355
+ end
321
356
  end
322
357
 
323
- raise ArgumentError, "Entry point out of bounds:\n#{inspect}" if @entry >= @height
324
-
325
- return unless @exit >= @height
326
-
327
- raise ArgumentError, "Exit point out of bounds:\n#{inspect}"
358
+ if show
359
+ puts result
360
+ else
361
+ result
362
+ end
328
363
  end
329
364
 
365
+ private
366
+
330
367
  def inspect
331
368
  output = ["TextDiagram(entry=#{@entry}, exit=#{@exit}, height=#{@height})"]
332
369
  @lines.each_with_index do |line, i|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailroadDiagrams
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/railroad_diagrams/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'railroad_diagrams'
7
+ spec.version = RailroadDiagrams::VERSION
8
+ spec.authors = ['Yudai Takada']
9
+ spec.email = ['t.yudai92@gmail.com']
10
+
11
+ spec.summary = '🛤️ A tiny Ruby+SVG library for drawing railroad syntax diagrams like JSON.org.'
12
+ spec.description = 'Generate SVG railroad syntax diagrams, like on JSON.org.'
13
+ spec.homepage = 'https://github.com/ydah/railroad_diagrams'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/releases"
20
+ spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
21
+ spec.metadata['rubygems_mfa_required'] = 'true'
22
+
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features|sample)/}) }
25
+ end
26
+
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+ end
data/test.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  add('test choice up',
2
4
  Diagram.new(
3
5
  Choice.new(
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railroad_diagrams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-02-01 00:00:00.000000000 Z
10
+ date: 2025-02-24 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: Generate SVG railroad syntax diagrams, like on JSON.org.
14
13
  email:
@@ -18,7 +17,10 @@ executables:
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
20
+ - ".github/workflows/main.yml"
21
+ - ".gitignore"
21
22
  - CHANGELOG.md
23
+ - Gemfile
22
24
  - LICENSE.txt
23
25
  - MIT
24
26
  - README.md
@@ -50,7 +52,7 @@ files:
50
52
  - lib/railroad_diagrams/text_diagram.rb
51
53
  - lib/railroad_diagrams/version.rb
52
54
  - lib/railroad_diagrams/zero_or_more.rb
53
- - sample/sample.html
55
+ - railroad_diagrams.gemspec
54
56
  - test.rb
55
57
  homepage: https://github.com/ydah/railroad_diagrams
56
58
  licenses:
@@ -61,7 +63,6 @@ metadata:
61
63
  changelog_uri: https://github.com/ydah/railroad_diagrams/releases
62
64
  bug_tracker_uri: https://github.com/ydah/railroad_diagrams/issues
63
65
  rubygems_mfa_required: 'true'
64
- post_install_message:
65
66
  rdoc_options: []
66
67
  require_paths:
67
68
  - lib
@@ -76,8 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  - !ruby/object:Gem::Version
77
78
  version: '0'
78
79
  requirements: []
79
- rubygems_version: 3.3.3
80
- signing_key:
80
+ rubygems_version: 3.7.0.dev
81
81
  specification_version: 4
82
82
  summary: "\U0001F6E4️ A tiny Ruby+SVG library for drawing railroad syntax diagrams
83
83
  like JSON.org."