Class: RailroadDiagrams::Sequence

Inherits:
DiagramMultiContainer
  • Object
show all
Defined in:
lib/railroad_diagrams/sequence.rb,
sig/generated/railroad_diagrams/sequence.rbs

Overview

Draws items in reading order along one path.

Examples:

Sequence.new('SELECT', NonTerminal.new('column'))

Instance Method Summary collapse

Constructor Details

#initialize(*items, id: nil, cls: nil, attrs: {}) ⇒ Sequence

Returns a new instance of Sequence.

Parameters:

  • items (DiagramItem, String)
  • id: (Object) (defaults to: nil)
  • cls: (Object) (defaults to: nil)
  • attrs: (Object) (defaults to: {})


11
12
13
14
15
# File 'lib/railroad_diagrams/sequence.rb', line 11

def initialize(*items, id: nil, cls: nil, attrs: {})
  super('g', items, nil, nil, id: id, cls: cls, data_attrs: attrs)
  @needs_space = true
  calculate_dimensions
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)


149
150
151
152
# File 'lib/railroad_diagrams/sequence.rb', line 149

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

#add_leading_space(x, y, item, index) ⇒ Numeric

Parameters:

  • x (Numeric)
  • y (Numeric)
  • item (DiagramItem)
  • index (Integer)

Returns:

  • (Numeric)


182
183
184
185
186
187
# File 'lib/railroad_diagrams/sequence.rb', line 182

def add_leading_space(x, y, item, index)
  return x unless item.needs_space && index.positive?

  Path.new(x, y).h(10).add(self)
  x + 10
end

#add_trailing_space(x, y, item, index) ⇒ Numeric

Parameters:

  • x (Numeric)
  • y (Numeric)
  • item (DiagramItem)
  • index (Integer)

Returns:

  • (Numeric)


194
195
196
197
198
199
# File 'lib/railroad_diagrams/sequence.rb', line 194

def add_trailing_space(x, y, item, index)
  return x unless item.needs_space && index < @items.length - 1

  Path.new(x, y).h(10).add(self)
  x + 10
end

#calculate_dimensions ⇒ void

This method returns an undefined value.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/railroad_diagrams/sequence.rb', line 127

def calculate_dimensions
  @up = 0
  @down = 0
  @height = 0
  @width = 0

  @items.each do |item|
    @width += item.width + (item.needs_space ? 20 : 0)
    @up = [@up, item.up - @height].max
    @height += item.height
    @down = [@down - item.height, item.down].max
  end

  @width -= 10 if @items.first&.needs_space
  @width -= 10 if @items.last&.needs_space
end

#child_nodes ⇒ Array[DiagramItem]

Returns:



112
113
114
# File 'lib/railroad_diagrams/sequence.rb', line 112

def child_nodes
  @items.dup
end

#format(x, y, width) ⇒ Sequence

Parameters:

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

Returns:



27
28
29
30
31
32
# File 'lib/railroad_diagrams/sequence.rb', line 27

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

#format_items(x, y) ⇒ void

This method returns an undefined value.

Parameters:

  • x (Numeric)
  • y (Numeric)


157
158
159
160
161
# File 'lib/railroad_diagrams/sequence.rb', line 157

def format_items(x, y)
  @items.each_with_index do |item, i|
    x, y = format_single_item(item, i, x, y)
  end
end

#format_single_item(item, index, x, y) ⇒ [ Numeric, Numeric ]

Parameters:

  • item (DiagramItem)
  • index (Integer)
  • x (Numeric)
  • y (Numeric)

Returns:

  • ([ Numeric, Numeric ])


168
169
170
171
172
173
174
175
# File 'lib/railroad_diagrams/sequence.rb', line 168

def format_single_item(item, index, x, y)
  x = add_leading_space(x, y, item, index)
  item.format(x, y, item.width).add(self)
  x += item.width
  y += item.height
  x = add_trailing_space(x, y, item, index)
  [x, y]
end

#measure(context) ⇒ Metrics

Parameters:

  • context (Context)

Returns:

  • (Metrics)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/railroad_diagrams/sequence.rb', line 46

def measure(context)
  up = 0
  down = 0
  height = 0
  width = 0
  metrics = @items.map { |item| context.metrics(item) }

  metrics.each do |child|
    width += child.width + (child.needs_space ? 20 : 0)
    up = [up, child.up - height].max
    height += child.height
    down = [down - child.height, child.down].max
  end

  width -= 10 if metrics.first&.needs_space
  width -= 10 if metrics.last&.needs_space
  Metrics.new(width: width, up: up, height: height, down: down, needs_space: true)
end

#render_svg(context, x, y, width) ⇒ Svg::Element

Parameters:

  • context (Context)
  • x (Numeric)
  • y (Numeric)
  • width (Numeric)

Returns:

  • (Svg::Element)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/railroad_diagrams/sequence.rb', line 70

def render_svg(context, x, y, width)
  metrics = context.metrics(self)
  left_gap, right_gap = context.gaps(width, metrics.width)
  group = Svg::Element.new('g', @attrs.dup)
  group << svg_path(x, y, left_gap)
  group << svg_path(x + left_gap + metrics.width, y + metrics.height, right_gap)
  x += left_gap

  @items.each_with_index do |item, index|
    child = context.metrics(item)
    if child.needs_space && index.positive?
      group << svg_path(x, y, 10)
      x += 10
    end
    group << context.render_svg(item, x, y, child.width)
    x += child.width
    y += child.height
    if child.needs_space && index < @items.length - 1
      group << svg_path(x, y, 10)
      x += 10
    end
  end
  group
end

#render_text(context) ⇒ TextDiagram

Parameters:

  • context (Context)

Returns:

  • (TextDiagram)


97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/railroad_diagrams/sequence.rb', line 97

def render_text(context)
  line = context.parts.fetch('line')
  diagrams = @items.map do |item|
    diagram = item.render_text(context)
    next diagram unless context.metrics(item).needs_space

    spaced_lines = diagram.lines.each_with_index.map do |text, index|
      "#{index == diagram.entry ? line : ' '}#{text}#{index == diagram.exit ? line : ' '}"
    end
    TextDiagram.new(diagram.entry, diagram.exit, spaced_lines)
  end
  Text::Builder.row([TextDiagram.new(0, 0, ['']), *diagrams], context.parts.fetch('separator'))
end

#svg_path(x, y, length) ⇒ Svg::Element

Parameters:

  • x (Numeric)
  • y (Numeric)
  • length (Numeric)

Returns:

  • (Svg::Element)


122
123
124
# File 'lib/railroad_diagrams/sequence.rb', line 122

def svg_path(x, y, length)
  Svg::Element.new('path', { 'd' => Svg::PathData.new(x, y).h(length) }, self_closing: true)
end

#text_diagram ⇒ TextDiagram

Returns:

  • (TextDiagram)


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

def text_diagram
  separator, = TextDiagram.get_parts(['separator'])
  @items.reduce(TextDiagram.new(0, 0, [''])) do |diagram_td, item|
    item_td = item.text_diagram
    item_td = item_td.expand(1, 1, 0, 0) if item.needs_space
    diagram_td.append_right(item_td, separator)
  end
end

#to_s ⇒ String

Returns:

  • (String)


18
19
20
21
# File 'lib/railroad_diagrams/sequence.rb', line 18

def to_s
  items = @items.map(&:to_s).join(', ')
  "Sequence(#{items})"
end