Class: RailroadDiagrams::OneOrMore

Inherits:
DiagramItem show all
Defined in:
lib/railroad_diagrams/one_or_more.rb,
sig/generated/railroad_diagrams/one_or_more.rbs

Overview

Draws one or more occurrences of an item.

Examples:

OneOrMore.new('digit', ',')

Instance Attribute Summary

Attributes inherited from DiagramItem

#attrs, #children, #down, #height, #needs_space, #up, #width

Instance Method Summary collapse

Methods inherited from DiagramItem

#==, #add, #apply_user_attributes, #container_element?, #determine_gaps, #each_node, #hash, inherited, #inspect, #structural_state, #to_str, #wrap_string, #write_children, #write_opening_tag, #write_standalone, #write_svg

Constructor Details

#initialize(item, repeat = nil, id: nil, cls: nil, attrs: {}) ⇒ OneOrMore

Returns a new instance of OneOrMore.

Parameters:

  • item (DiagramItem, String)
  • repeat (?(DiagramItem | String), nil) (defaults to: nil)
  • id: (Object) (defaults to: nil)
  • cls: (Object) (defaults to: nil)
  • attrs: (Object) (defaults to: {})


12
13
14
15
16
17
18
19
20
21
22
# File 'lib/railroad_diagrams/one_or_more.rb', line 12

def initialize(item, repeat = nil, id: nil, cls: nil, attrs: {})
  super('g', id: id, cls: cls, data_attrs: attrs)
  @item = wrap_string(item)
  repeat ||= Skip.new
  @rep = wrap_string(repeat)
  @width = [@item.width, @rep.width].max + (AR * 2)
  @height = @item.height
  @up = @item.up
  @down = [AR * 2, @item.down + VS + @rep.up + @rep.height + @rep.down].max
  @needs_space = true
end

Instance Method Details

#add_edge_paths(x, y, left_gap, right_gap) ⇒ void

This method returns an undefined value.

Parameters:

  • x (Numeric)
  • y (Numeric)
  • left_gap (Numeric)
  • right_gap (Numeric)


156
157
158
159
# File 'lib/railroad_diagrams/one_or_more.rb', line 156

def add_edge_paths(x, y, left_gap, right_gap)
  Path.new(x, y).h(left_gap).add(self)
  Path.new(x + left_gap + @width, y + @height).h(right_gap).add(self)
end

#child_nodes ⇒ Object

Returns:

  • (Object)


133
134
135
# File 'lib/railroad_diagrams/one_or_more.rb', line 133

def child_nodes
  [@item, @rep]
end

#draw_main_item(x, y) ⇒ void

This method returns an undefined value.

Parameters:

  • x (Numeric)
  • y (Numeric)


164
165
166
167
168
# File 'lib/railroad_diagrams/one_or_more.rb', line 164

def draw_main_item(x, y)
  Path.new(x, y).right(AR).add(self)
  @item.format(x + AR, y, @width - (AR * 2)).add(self)
  Path.new(x + @width - AR, y + @height).right(AR).add(self)
end

#draw_repeat_arc(x, y) ⇒ void

This method returns an undefined value.

Parameters:

  • x (Numeric)
  • y (Numeric)


173
174
175
176
177
178
179
180
181
182
183
# File 'lib/railroad_diagrams/one_or_more.rb', line 173

def draw_repeat_arc(x, y)
  distance_from_y = [AR * 2, @item.height + @item.down + VS + @rep.up].max

  Path.new(x + AR, y).arc('nw').down(distance_from_y - (AR * 2)).arc('ws').add(self)
  @rep.format(x + AR, y + distance_from_y, @width - (AR * 2)).add(self)
  Path.new(x + @width - AR, y + distance_from_y + @rep.height)
      .arc('se')
      .up(distance_from_y - (AR * 2) + @rep.height - @item.height)
      .arc('en')
      .add(self)
end

#format(x, y, width) ⇒ OneOrMore

Parameters:

  • x (Numeric)
  • y (Numeric)
  • width (Numeric)

Returns:



33
34
35
36
37
38
39
40
41
42
# File 'lib/railroad_diagrams/one_or_more.rb', line 33

def format(x, y, width)
  left_gap, right_gap = determine_gaps(width, @width)
  add_edge_paths(x, y, left_gap, right_gap)
  x += left_gap

  draw_main_item(x, y)
  draw_repeat_arc(x, y)

  self
end

#measure(context) ⇒ Object

Parameters:

  • context (Object)

Returns:

  • (Object)


75
76
77
78
79
80
81
82
83
84
# File 'lib/railroad_diagrams/one_or_more.rb', line 75

def measure(context)
  item = context.metrics(@item)
  repeat = context.metrics(@rep)
  arc = context.options.arc_radius
  separation = context.options.vertical_separation
  Metrics.new(width: [item.width, repeat.width].max + (arc * 2),
              up: item.up, height: item.height,
              down: [arc * 2, item.down + separation + repeat.up + repeat.height + repeat.down].max,
              needs_space: true)
end

#render_svg(context, x, y, width) ⇒ Object

Parameters:

  • context (Object)
  • x (Object)
  • y (Object)
  • width (Object)

Returns:

  • (Object)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/railroad_diagrams/one_or_more.rb', line 86

def render_svg(context, x, y, width)
  metrics = context.metrics(self)
  item = context.metrics(@item)
  repeat = context.metrics(@rep)
  arc = context.options.arc_radius
  separation = context.options.vertical_separation
  left_gap, right_gap = context.gaps(width, metrics.width)
  group = Svg::Element.new('g', @attrs.dup)
  group << svg_path(Svg::PathData.new(x, y).h(left_gap))
  group << svg_path(Svg::PathData.new(x + left_gap + metrics.width, y + metrics.height).h(right_gap))
  x += left_gap
  group << svg_path(Svg::PathData.new(x, y).h(arc))
  group << context.render_svg(@item, x + arc, y, metrics.width - (arc * 2))
  group << svg_path(Svg::PathData.new(x + metrics.width - arc, y + metrics.height).h(arc))

  distance = [arc * 2, item.height + item.down + separation + repeat.up].max
  group << svg_path(Svg::PathData.new(x + arc, y, arc_radius: arc).arc('nw').v([0, distance - (arc * 2)].max).arc('ws'))
  group << context.render_svg(@rep, x + arc, y + distance, metrics.width - (arc * 2))
  climb = distance - (arc * 2) + repeat.height - item.height
  group << svg_path(Svg::PathData.new(x + metrics.width - arc, y + distance + repeat.height, arc_radius: arc)
    .arc('se').v(-[0, climb].max).arc('en'))
  group
end

#render_text(context) ⇒ Object

Parameters:

  • context (Object)

Returns:

  • (Object)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/railroad_diagrams/one_or_more.rb', line 110

def render_text(context)
  line, top_left, left, bottom_left, top_right, right, bottom_right = context.parts.values_at(
    'line', 'repeat_top_left', 'repeat_left', 'repeat_bot_left',
    'repeat_top_right', 'repeat_right', 'repeat_bot_right'
  )
  item = @item.render_text(context)
  repeat = @rep.render_text(context)
  width = TextDiagram.max_width(item, repeat)
  repeat = repeat.expand(0, width - repeat.width, 0, 0)
  item = item.expand(0, width - item.width, 0, 0)
  both = item.append_below(repeat, [])

  left_lines = [top_left + line]
  left_lines += ["#{left} "] * ((item.height - item.entry) + repeat.entry - 1)
  left_lines << (bottom_left + line)
  diagram = TextDiagram.new(0, 0, left_lines).append_right(both, '')

  right_lines = [line + top_right]
  right_lines += [" #{right}"] * ((item.height - item.exit) + repeat.exit - 1)
  right_lines << (line + bottom_right)
  diagram.append_right(TextDiagram.new(0, 0, right_lines), '')
end

#svg_path(data) ⇒ Object

Parameters:

  • data (Object)

Returns:

  • (Object)


147
148
149
# File 'lib/railroad_diagrams/one_or_more.rb', line 147

def svg_path(data)
  Svg::Element.new('path', { 'd' => data }, self_closing: true)
end

#text_diagram ⇒ TextDiagram

Returns:

  • (TextDiagram)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/railroad_diagrams/one_or_more.rb', line 45

def text_diagram
  parts = TextDiagram.get_parts(
    %w[
      line repeat_top_left repeat_left repeat_bot_left repeat_top_right repeat_right repeat_bot_right
    ]
  )
  line, repeat_top_left, repeat_left, repeat_bot_left, repeat_top_right, repeat_right, repeat_bot_right = parts

  item_td = @item.text_diagram
  repeat_td = @rep.text_diagram
  fir_width = TextDiagram.max_width(item_td, repeat_td)
  repeat_td = repeat_td.expand(0, fir_width - repeat_td.width, 0, 0)
  item_td = item_td.expand(0, fir_width - item_td.width, 0, 0)
  item_and_repeat_td = item_td.append_below(repeat_td, [])

  left_lines = []
  left_lines << (repeat_top_left + line)
  left_lines += ["#{repeat_left} "] * ((item_td.height - item_td.entry) + repeat_td.entry - 1)
  left_lines << (repeat_bot_left + line)
  left_td = TextDiagram.new(0, 0, left_lines)
  left_td = left_td.append_right(item_and_repeat_td, '')

  right_lines = []
  right_lines << (line + repeat_top_right)
  right_lines += [" #{repeat_right}"] * ((item_td.height - item_td.exit) + repeat_td.exit - 1)
  right_lines << (line + repeat_bot_right)
  right_td = TextDiagram.new(0, 0, right_lines)
  left_td.append_right(right_td, '')
end

#to_s ⇒ String

Returns:

  • (String)


25
26
27
# File 'lib/railroad_diagrams/one_or_more.rb', line 25

def to_s
  "OneOrMore(#{@item}, repeat=#{@rep})"
end

#walk(callback) ⇒ void

This method returns an undefined value.



139
140
141
142
143
# File 'lib/railroad_diagrams/one_or_more.rb', line 139

def walk(callback)
  callback.call(self)
  @item.walk(callback)
  @rep.walk(callback)
end