railroad_diagrams 0.2.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6fa68a911b60026d1ad25654c32a26e5f0f2e972e62361b23d451a4f9a97631
4
- data.tar.gz: 24048fdb5ee0ffb805f5581f37035f37f7fab2328ba15aec5134590de5f5db1d
3
+ metadata.gz: 3e08635bb1091eae639e0c6579810827a46b42dc7ab39856df0167c5a771b651
4
+ data.tar.gz: d0b8e359534ca8556b056e16323405bad2d187b35862f1198cc333e2909679ed
5
5
  SHA512:
6
- metadata.gz: cab2582164d955d3761eabb1f28b663b9dcd28399309259e225560b9d142eff2f92963dc4e9f118a6bfe21be1dba2e67c452638e8de63ce1282c6db30d4052d5
7
- data.tar.gz: d7babd2c0e30c60fedd4a3ca638eec5169ffd3d60418abe36478f256010f0354b857b7c9891ad90991b728b9cfa6ea926bf948b2ccec0f4d10cd723d7a09345f
6
+ metadata.gz: e0e40fcbe169c00fb808bbc479e59c9d2b52bb132f7d096ec228d82c8a594ef207fdb01fa4a7f68445937a0b6cda2f592c6c936215434ab1fb65ca86ea7a3300
7
+ data.tar.gz: 0355b014fc8baa9138dbabc81c2cdf338cabc7b0c8905563078b702b8765cabfd2475b281673eceffc5b6fd7684eb8e6f587c545621364a406b57509157aaa4a
@@ -26,4 +26,8 @@ jobs:
26
26
  with:
27
27
  ruby-version: ${{ matrix.ruby }}
28
28
  bundler-cache: true
29
- - run: exe/railroad_diagrams --format=svg
29
+ - run: |
30
+ exe/railroad_diagrams --format=svg
31
+ exe/railroad_diagrams --format=ascii
32
+ exe/railroad_diagrams --format=unicode
33
+ exe/railroad_diagrams --format=standalone
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.0 - 2025-02-24
6
+
7
+ - Fix some error when using ascii and unicode mode.
8
+
5
9
  ## 0.2.1 - 2025-02-01
6
10
 
7
11
  - Fix an error for bundle install from Ruby 2.5.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A tiny Ruby+SVG library for drawing railroad syntax diagrams.
4
4
 
5
+ Inspired by: https://github.com/tabatkins/railroad-diagrams
6
+
5
7
  # Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -115,14 +115,14 @@ module RailroadDiagrams
115
115
 
116
116
  first_td = @items[0].text_diagram
117
117
  second_td = @items[1].text_diagram
118
- max_width = TextDiagram._max_width(first_td, second_td)
119
- left_width, right_width = TextDiagram._gaps(max_width, 0)
118
+ max_width = TextDiagram.max_width(first_td, second_td)
119
+ left_width, right_width = TextDiagram.gaps(max_width, 0)
120
120
 
121
121
  left_lines = []
122
122
  right_lines = []
123
123
  separator = []
124
124
 
125
- left_size, right_size = TextDiagram._gaps(first_td.width, 0)
125
+ left_size, right_size = TextDiagram.gaps(first_td.width, 0)
126
126
  diagram_td = first_td.expand(left_width - left_size, right_width - right_size, 0, 0)
127
127
 
128
128
  left_lines += [' ' * 2] * diagram_td.entry
@@ -142,7 +142,7 @@ module RailroadDiagrams
142
142
  left_lines << (' ' * 2)
143
143
  right_lines << (' ' * 2)
144
144
 
145
- left_size, right_size = TextDiagram._gaps(second_td.width, 0)
145
+ left_size, right_size = TextDiagram.gaps(second_td.width, 0)
146
146
  second_td = second_td.expand(left_width - left_size, right_width - right_size, 0, 0)
147
147
  diagram_td = diagram_td.append_below(second_td, separator, move_entry: true, move_exit: true)
148
148
 
@@ -155,7 +155,7 @@ module RailroadDiagrams
155
155
  right_lines << (line + corner_bot_right)
156
156
 
157
157
  mid_point = first_td.height + (separator.size / 2)
158
- diagram_td = diagram_td.alter(entry: mid_point, exit: mid_point)
158
+ diagram_td = diagram_td.alter(new_entry: mid_point, new_exit: mid_point)
159
159
 
160
160
  left_td = TextDiagram.new(mid_point, mid_point, left_lines)
161
161
  right_td = TextDiagram.new(mid_point, mid_point, right_lines)
@@ -40,8 +40,6 @@ module RailroadDiagrams
40
40
  @up += @items[0].up
41
41
 
42
42
  @height = @items[default].height
43
- @down = 0
44
-
45
43
  (default + 1...@items.size).each do |i|
46
44
  arcs =
47
45
  if i == default + 1
@@ -67,7 +65,7 @@ module RailroadDiagrams
67
65
  end
68
66
 
69
67
  def to_s
70
- items_str = @items.map(&:inspect).join(', ')
68
+ items_str = @items.map(&:to_s).join(', ')
71
69
  "Choice(#{@default}, #{items_str})"
72
70
  end
73
71
 
@@ -150,57 +148,62 @@ module RailroadDiagrams
150
148
  move_entry = false
151
149
  move_exit = false
152
150
  if i <= @default
153
- # First item and above the line: also remove ascenders above the item's entry and exit, suppress the separator above it.
154
- has_separator = false
155
- [0..item_td.entry].each { |j| left_lines[j] = ' ' }
156
- [0..item_td.exit].each { |j| right_lines[j] = ' ' }
151
+ # Item below the line: round off the entry/exit lines downwards.
152
+ left_lines[item_td.entry] = roundcorner_top_left
153
+ right_lines[item_td.exit] = roundcorner_top_right
154
+ if i.zero?
155
+ # First item and above the line: also remove ascenders above the item's entry and exit, suppress the separator above it.
156
+ has_separator = false
157
+ (0...item_td.entry).each { |j| left_lines[j] = ' ' }
158
+ (0...item_td.exit).each { |j| right_lines[j] = ' ' }
159
+ end
157
160
  end
158
161
  if i >= @default
159
162
  # Item below the line: round off the entry/exit lines downwards.
160
163
  left_lines[item_td.entry] = roundcorner_bot_left
161
- right_lines[item_td.entry] = roundcorner_bot_right
162
- if i == 0
164
+ right_lines[item_td.exit] = roundcorner_bot_right
165
+ if i.zero?
163
166
  # First item and below the line: also suppress the separator above it.
164
167
  has_separator = false
165
168
  end
166
169
  if i == @items.size - 1
167
170
  # Last item and below the line: also remove descenders below the item's entry and exit
168
- [item_td.entry + 1..item_td.height].each { |j| left_lines[j] = ' ' }
169
- [item_td.exit + 1..item_td.height].each { |j| right_lines[j] = ' ' }
171
+ (item_td.entry + 1...item_td.height).each { |j| left_lines[j] = ' ' }
172
+ (item_td.exit + 1...item_td.height).each { |j| right_lines[j] = ' ' }
170
173
  end
171
174
  end
172
175
  if i == @default
173
176
  # Item on the line: entry/exit are horizontal, and sets the outer entry/exit.
174
177
  left_lines[item_td.entry] = cross
175
- right_lines[item_td.entry] = cross
178
+ right_lines[item_td.exit] = cross
176
179
  move_entry = true
177
180
  move_exit = true
178
- if i == 0 && i == @items.size - 1
181
+ if i.zero? && i == @items.size - 1
179
182
  # Only item and on the line: set entry/exit for straight through.
180
183
  left_lines[item_td.entry] = line
181
- right_lines[item_td.entry] = line
182
- elsif i == 0
184
+ right_lines[item_td.exit] = line
185
+ elsif i.zero?
183
186
  # First item and on the line: set entry/exit for no ascenders.
184
- left_lines[item_td.entry] = roundcorner_top_left
185
- left_lines[item_td.exit] = roundcorner_bot_left
187
+ left_lines[item_td.entry] = roundcorner_top_right
188
+ right_lines[item_td.exit] = roundcorner_top_left
186
189
  elsif i == @items.size - 1
187
190
  # Last item and on the line: set entry/exit for no descenders.
188
- left_lines[item_td.entry] = roundcorner_bot_left
189
- right_lines[item_td.entry] = roundcorner_bot_right
191
+ left_lines[item_td.entry] = roundcorner_bot_right
192
+ right_lines[item_td.exit] = roundcorner_bot_left
190
193
  end
191
194
  end
192
195
  left_join_td = TextDiagram.new(item_td.entry, item_td.entry, left_lines)
193
196
  right_join_td = TextDiagram.new(item_td.exit, item_td.exit, right_lines)
194
197
  item_td = left_join_td.append_right(item_td, '').append_right(right_join_td, '')
195
- separator = if has_separator
196
- [
197
- line_vertical +
198
- (' ' * (TextDiagram.max_width(diagram_td, item_td) - 2)) +
199
- line_vertical
200
- ]
201
- else
202
- []
203
- end
198
+ separator =
199
+ if has_separator
200
+ [
201
+ line_vertical +
202
+ (' ' * (TextDiagram.max_width(diagram_td, item_td) - 2)) + line_vertical
203
+ ]
204
+ else
205
+ []
206
+ end
204
207
  diagram_td = diagram_td.append_below(item_td, separator, move_entry: move_entry, move_exit: move_exit)
205
208
  end
206
209
  diagram_td
@@ -69,12 +69,12 @@ module RailroadDiagrams
69
69
 
70
70
  case @format
71
71
  when 'svg'
72
- diagram.write_svg(STDOUT.method(:write))
72
+ diagram.write_svg($stdout.method(:write))
73
73
  when 'standalone'
74
- diagram.write_standalone(STDOUT.method(:write))
74
+ diagram.write_standalone($stdout.method(:write))
75
75
  when 'ascii', 'unicode'
76
76
  puts "\n<pre>"
77
- diagram.write_text(STDOUT.method(:write))
77
+ diagram.write_text($stdout.method(:write))
78
78
  puts "\n</pre>"
79
79
  end
80
80
 
@@ -42,7 +42,7 @@ module RailroadDiagrams
42
42
 
43
43
  def text_diagram
44
44
  # NOTE: href, title, and cls are ignored for text diagrams.
45
- TextDiagram.new(0, 0, @text)
45
+ TextDiagram.new(0, 0, [@text])
46
46
  end
47
47
  end
48
48
  end
@@ -74,9 +74,10 @@ module RailroadDiagrams
74
74
  diagram_td = @items[0].text_diagram
75
75
  @items[1..-1].each do |item|
76
76
  item_td = item.text_diagram
77
- item_td.expand(1, 1, 0, 0) if item.needs_space
78
- diagram_td = diagram_td.append_right(separator)
77
+ item_td = item_td.expand(1, 1, 0, 0) if item.needs_space
78
+ diagram_td = diagram_td.append_right(item_td, separator)
79
79
  end
80
+ diagram_td
80
81
  end
81
82
 
82
83
  def write_svg(write)
@@ -88,8 +89,8 @@ module RailroadDiagrams
88
89
  def write_text(write)
89
90
  output = text_diagram
90
91
  output = "#{output.lines.join("\n")}\n"
91
- output = output.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;').gsub('"', '&quot;') if ESCAPE_HTML
92
- write(output)
92
+ output = output.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;').gsub('"', '&quot;')
93
+ write.call(output)
93
94
  end
94
95
 
95
96
  def write_standalone(write, css = nil)
@@ -74,7 +74,7 @@ module RailroadDiagrams
74
74
  end
75
75
  end
76
76
 
77
- def write_standalone(write, css = nil)
77
+ def write_standalone(write, _css = nil)
78
78
  write_svg(write)
79
79
  end
80
80
  end
@@ -52,7 +52,7 @@ module RailroadDiagrams
52
52
  ).add(self)
53
53
 
54
54
  @item.format(x, y, @width).add(self)
55
- @label.format(x, y - (@box_up + @label.down + @label.height), @width).add(self) if @label
55
+ @label&.format(x, y - (@box_up + @label.down + @label.height), @width)&.add(self)
56
56
 
57
57
  self
58
58
  end
@@ -20,7 +20,7 @@ module RailroadDiagrams
20
20
  AR + # starting track
21
21
  (AR * 2 * (@items.size - 1)) + # inbetween tracks
22
22
  @items.sum { |x| x.width + (x.needs_space ? 20 : 0) } + # items
23
- (last.height > 0 ? AR : 0) + # needs space to curve up
23
+ (last.height.positive? ? AR : 0) + # needs space to curve up
24
24
  AR # ending track
25
25
 
26
26
  # Always exits at entrance height
@@ -156,31 +156,31 @@ module RailroadDiagrams
156
156
  # diagram_entry: distance from top to lowest entry, aka distance from top to diagram entry, aka final diagram entry and exit.
157
157
  diagram_entry = item_tds.map(&:entry).max
158
158
  # soil_to_baseline: distance from top to lowest entry before rightmost item, aka distance from skip-over-items line to rightmost entry, aka SOIL height.
159
- soil_to_baseline = item_tds[0..-2].map(&:entry).max || 0
159
+ soil_to_baseline = item_tds[0...-1].map(&:entry).max
160
160
  # top_to_soil: distance from top to skip-over-items line.
161
161
  top_to_soil = diagram_entry - soil_to_baseline
162
162
  # baseline_to_suil: distance from lowest entry or exit after leftmost item to bottom, aka distance from entry to skip-under-items line, aka SUIL height.
163
- baseline_to_suil = item_tds[1..-1].map { |td| td.height - [td.entry, td.exit].min }.max.to_i - 1
163
+ baseline_to_suil = item_tds[1..-1].map { |td| td.height - [td.entry, td.exit].min }.max - 1
164
164
 
165
165
  # The diagram starts with a line from its entry up to skip-over-items line:
166
166
  lines = Array.new(top_to_soil, ' ')
167
167
  lines << (roundcorner_top_left + line)
168
- lines += Array.new(soil_to_baseline, line_vertical + ' ')
168
+ lines += Array.new(soil_to_baseline, "#{line_vertical} ")
169
169
  lines << (roundcorner_bot_right + line)
170
170
 
171
171
  diagram_td = TextDiagram.new(lines.size - 1, lines.size - 1, lines)
172
172
 
173
173
  item_tds.each_with_index do |item_td, item_num|
174
- if item_num > 0
174
+ if item_num.positive?
175
175
  # All items except the leftmost start with a line from the skip-over-items line down to their entry,
176
176
  # with a joining-line across at the skip-under-items line:
177
- lines = Array.new(top_to_soil, ' ')
177
+ lines = [' '] * top_to_soil
178
178
  # All such items except the rightmost also have a continuation of the skip-over-items line:
179
179
  line_to_next_item = item_num == item_tds.size - 1 ? ' ' : line
180
180
  lines << (roundcorner_top_right + line_to_next_item)
181
- lines += Array.new(soil_to_baseline, line_vertical + ' ')
181
+ lines += ["#{line_vertical} "] * soil_to_baseline
182
182
  lines << (roundcorner_bot_left + line)
183
- lines += Array.new(baseline_to_suil, ' ')
183
+ lines += [' '] * baseline_to_suil
184
184
  lines << (line * 2)
185
185
 
186
186
  entry_td = TextDiagram.new(diagram_td.exit, diagram_td.exit, lines)
@@ -201,7 +201,7 @@ module RailroadDiagrams
201
201
 
202
202
  part_td = part_td.append_below(item_td, [], move_entry: true, move_exit: true)
203
203
 
204
- if item_num > 0
204
+ if item_num.positive?
205
205
  # All items except the leftmost end with enough blank lines to pad down to the skip-under-items
206
206
  # line, followed by a segment of the skip-under-items line:
207
207
  lines = Array.new(baseline_to_suil - (item_td.height - item_td.entry) + 1, ' ' * item_td.width)
@@ -219,26 +219,25 @@ module RailroadDiagrams
219
219
  lines << (line * 2)
220
220
  lines += Array.new(diagram_td.exit - top_to_soil - 1, ' ')
221
221
  lines << (line + roundcorner_top_right)
222
- lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), ' ' + line_vertical)
223
- line_from_prev_item = item_num > 0 ? line : ' '
222
+ lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), " #{line_vertical}")
223
+ line_from_prev_item = item_num.positive? ? line : ' '
224
224
  lines << (line_from_prev_item + roundcorner_bot_left)
225
225
 
226
226
  entry = diagram_entry + 1 + (diagram_td.exit - diagram_td.entry)
227
227
  exit_td = TextDiagram.new(entry, diagram_entry + 1, lines)
228
- diagram_td = diagram_td.append_right(exit_td, '')
229
228
  else
230
229
  # The rightmost item has a line from the skip-under-items line and from its exit up to the diagram exit:
231
230
  lines = []
232
231
  line_from_exit = diagram_td.exit == diagram_td.entry ? line : ' '
233
232
  lines << (line_from_exit + roundcorner_top_left)
234
- lines += Array.new(diagram_td.exit - diagram_td.entry - 1, ' ' + line_vertical)
233
+ lines += Array.new(diagram_td.exit - diagram_td.entry, " #{line_vertical}")
235
234
  lines << (line + roundcorner_bot_right) if diagram_td.exit != diagram_td.entry
236
- lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), ' ' + line_vertical)
235
+ lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), " #{line_vertical}")
237
236
  lines << (line + roundcorner_bot_right)
238
237
 
239
238
  exit_td = TextDiagram.new(diagram_td.exit - diagram_td.entry, 0, lines)
240
- diagram_td = diagram_td.append_right(exit_td, '')
241
239
  end
240
+ diagram_td = diagram_td.append_right(exit_td, '')
242
241
  end
243
242
 
244
243
  diagram_td
@@ -124,7 +124,7 @@ module RailroadDiagrams
124
124
  def text_diagram
125
125
  multi_repeat = TextDiagram.get_parts(['multi_repeat']).first
126
126
  any_all = TextDiagram.rect(@type == 'any' ? '1+' : 'all')
127
- diagram_td = Choice.text_diagram(self)
127
+ diagram_td = Choice.new(0, Skip.new).text_diagram
128
128
  repeat_td = TextDiagram.rect(multi_repeat)
129
129
  diagram_td = any_all.append_right(diagram_td, '')
130
130
  diagram_td.append_right(repeat_td, '')
@@ -55,7 +55,7 @@ module RailroadDiagrams
55
55
  # Format the item and then format the repeat append it to the bottom, after a spacer.
56
56
  item_td = @item.text_diagram
57
57
  repeat_td = @rep.text_diagram
58
- fir_width = TextDiagram._max_width(item_td, repeat_td)
58
+ fir_width = TextDiagram.max_width(item_td, repeat_td)
59
59
  repeat_td = repeat_td.expand(0, fir_width - repeat_td.width, 0, 0)
60
60
  item_td = item_td.expand(0, fir_width - item_td.width, 0, 0)
61
61
  item_and_repeat_td = item_td.append_below(repeat_td, [])
@@ -63,7 +63,7 @@ module RailroadDiagrams
63
63
  # Build the left side of the repeat line and append the combined item and repeat to its right.
64
64
  left_lines = []
65
65
  left_lines << (repeat_top_left + line)
66
- left_lines += [repeat_left + ' '] * ((item_td.height - item_td.entry) + repeat_td.entry - 1)
66
+ left_lines += ["#{repeat_left} "] * ((item_td.height - item_td.entry) + repeat_td.entry - 1)
67
67
  left_lines << (repeat_bot_left + line)
68
68
  left_td = TextDiagram.new(0, 0, left_lines)
69
69
  left_td = left_td.append_right(item_and_repeat_td, '')
@@ -71,7 +71,7 @@ module RailroadDiagrams
71
71
  # Build the right side of the repeat line and append it to the combined left side, item, and repeat's right.
72
72
  right_lines = []
73
73
  right_lines << (line + repeat_top_right)
74
- right_lines += [' ' + repeat_right] * ((item_td.height - item_td.exit) + repeat_td.exit - 1)
74
+ right_lines += [" #{repeat_right}"] * ((item_td.height - item_td.exit) + repeat_td.exit - 1)
75
75
  right_lines << (line + repeat_bot_right)
76
76
  right_td = TextDiagram.new(0, 0, right_lines)
77
77
  left_td.append_right(right_td, '')
@@ -136,7 +136,7 @@ module RailroadDiagrams
136
136
  # diagramEntry: distance from top to lowest entry, aka distance from top to diagram entry, aka final diagram entry and exit.
137
137
  diagram_entry = item_tds.map(&:entry).max
138
138
  # SOILHeight: distance from top to lowest entry before rightmost item, aka distance from skip-over-items line to rightmost entry, aka SOIL height.
139
- soil_height = item_tds[0...-1].map(&:entry).max
139
+ soil_height = item_tds.map(&:entry).max
140
140
  # topToSOIL: distance from top to skip-over-items line.
141
141
  top_to_soil = diagram_entry - soil_height
142
142
 
@@ -147,7 +147,7 @@ module RailroadDiagrams
147
147
  lines += [roundcorner_bot_right + line]
148
148
  diagram_td = TextDiagram.new(lines.size - 1, lines.size - 1, lines)
149
149
 
150
- @items.each_with_index do |item_td, i|
150
+ item_tds.each_with_index do |item_td, i|
151
151
  if i.positive?
152
152
  # All items except the leftmost start with a line from their entry down to their skip-under-item line,
153
153
  # with a joining-line across at the skip-over-items line:
@@ -162,9 +162,10 @@ module RailroadDiagrams
162
162
 
163
163
  # All items except the leftmost next have a line from skip-over-items line down to their entry,
164
164
  # with joining-lines at their entry and at their skip-under-item line:
165
- lines = ([' '] * top_to_soil) + [line + roundcorner_top_right +
166
- # All such items except the rightmost also have a continuation of the skip-over-items line:
167
- (i < item_tds.size - 1 ? line : ' ')] +
165
+ lines = ([' '] * top_to_soil) +
166
+ [line + roundcorner_top_right +
167
+ # All such items except the rightmost also have a continuation of the skip-over-items line:
168
+ (i < item_tds.size - 1 ? line : ' ')] +
168
169
  ([" #{line_vertical} "] * (diagram_td.exit - top_to_soil - 1)) +
169
170
  [line + roundcorner_bot_left + line] +
170
171
  ([' ' * 3] * (item_td.height - item_td.entry - 1)) +
@@ -115,6 +115,7 @@ module RailroadDiagrams
115
115
  diagram_td = left_td.append_right(diagram_td, '')
116
116
  right_td = TextDiagram.new(0, right_lines.size - 1, right_lines)
117
117
  diagram_td.append_right(right_td, '')
118
+ diagram_td
118
119
  end
119
120
  end
120
121
  end
@@ -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.1'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -10,14 +10,14 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = '🛤️ A tiny Ruby+SVG library for drawing railroad syntax diagrams like JSON.org.'
12
12
  spec.description = 'Generate SVG railroad syntax diagrams, like on JSON.org.'
13
- spec.homepage = "https://github.com/ydah/railroad_diagrams"
13
+ spec.homepage = 'https://github.com/ydah/railroad_diagrams'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
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"
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
21
  spec.metadata['rubygems_mfa_required'] = 'true'
22
22
 
23
23
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
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,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railroad_diagrams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-01 00:00:00.000000000 Z
10
+ date: 2025-02-24 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Generate SVG railroad syntax diagrams, like on JSON.org.
13
13
  email: