Module: Formatter::Common

Included in:
RainbowFormatter
Defined in:
lib/formatter/common.rb

Constant Summary collapse

ESC =
"\e["
NND =
"#{ESC}0m"
PASS =
'='
PASS_ARY =
['-', '_'].freeze
FAIL =
'*'
ERROR =
'!'
PENDING =
'+'
VT100_CODES =
{
  black: 30,
  red: 31,
  green: 32,
  yellow: 33,
  blue: 34,
  magenta: 35,
  cyan: 36,
  white: 37,
  bold: 1
}.freeze
VT100_CODE_VALUES =
VT100_CODES.invert

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



28
29
30
31
32
# File 'lib/formatter/common.rb', line 28

def self.included(base)
  base.class_eval do
    attr_reader :current, :example_results, :color_index, :pending_count, :failure_count, :example_count
  end
end

Instance Method Details

#ascii_lengthObject

Returns the ascii length



215
216
217
# File 'lib/formatter/common.rb', line 215

def ascii_length
  ascii_to_display.split("\n").group_by(&:size).max.first
end

#ascii_to_displayString

Determine which Ascii to format(display.

Returns:

  • (String)

    Ascii to display



47
48
49
# File 'lib/formatter/common.rb', line 47

def ascii_to_display
  ascii_array[@animation_index % ascii_array.size]
end

#colorsArray

Calculates the colors of the rainbow

Returns:

  • (Array)


160
161
162
163
164
165
166
167
168
169
# File 'lib/formatter/common.rb', line 160

def colors
  @colors ||= (0...(6 * 7)).map do |n|
    pi3 = Math::PI / 3
    n *= 1.0 / 6
    r  = (3 * Math.sin(n) + 3).to_i
    g  = (3 * Math.sin(n + 2 * pi3) + 3).to_i
    b  = (3 * Math.sin(n + 4 * pi3) + 3).to_i
    36 * r + 6 * g + b + 16
  end
end

#console_code_for(code_or_symbol) ⇒ Object



231
232
233
234
235
236
237
238
239
# File 'lib/formatter/common.rb', line 231

def console_code_for(code_or_symbol)
  if VT100_CODE_VALUES.key?(code_or_symbol)
    code_or_symbol
  else
    VT100_CODES.fetch(code_or_symbol) do
      console_code_for(:white)
    end
  end
end

#current_widthFixnum

Calculates the current flight length

Returns:

  • (Fixnum)


92
93
94
95
# File 'lib/formatter/common.rb', line 92

def current_width
  # padding_width + example_width + ascii_length
  padding_width + (@current * example_width) + ascii_length
end

#dump_progressObject

Displays the current progress in all Rainbow glory

Returns:

  • nothing



54
55
56
# File 'lib/formatter/common.rb', line 54

def dump_progress
  output.print(progress_lines.join("\n") + eol)
end

#eolString

Determines how we end the trail line. If complete, return a newline etc.

Returns:

  • (String)


82
83
84
85
86
87
# File 'lib/formatter/common.rb', line 82

def eol
  return "\n" if @current == @example_count

  length = progress_lines.length - 1
  length.positive? ? format("\e[1A" * length + "\r") : "\r"
end

#example_width(_item = 1) ⇒ Object

Times a mark has to be repeated



143
144
145
# File 'lib/formatter/common.rb', line 143

def example_width(_item = 1)
  1
end

#failed_or_pending?Boolean

Determines if the any specs failed or are in pending state

Returns:

  • (Boolean)


208
209
210
# File 'lib/formatter/common.rb', line 208

def failed_or_pending?
  (@failure_count.to_i.positive? || @pending_count.to_i.positive?)
end

#failure_color(text) ⇒ Object



227
228
229
# File 'lib/formatter/common.rb', line 227

def failure_color(text)
  wrap(text, :red)
end

#finished?Boolean

Determines if the specs have completed

Returns:

  • (Boolean)


201
202
203
# File 'lib/formatter/common.rb', line 201

def finished?
  (@current == @example_count)
end

#format_duration(duration) ⇒ String

Converts a float of seconds into a minutes/seconds string

Returns:

  • (String)


188
189
190
191
192
193
194
195
196
# File 'lib/formatter/common.rb', line 188

def format_duration(duration)
  seconds = ((duration % 60) * 100.0).round / 100.0   # 1.8.7 safe .round(2)
  seconds = seconds.to_i if seconds.to_i == seconds   # drop that zero if it's not needed

  message = "#{seconds} second#{seconds == 1 ? '' : 's'}"
  message = "#{(duration / 60).to_i} minute#{(duration / 60).to_i == 1 ? '' : 's'} and " + message if duration >= 60

  message
end

#highlight(mark = PASS) ⇒ String

Determines how to color the example. If pass, it is rainbowified, otherwise we assign red if failed or yellow if an error occurred.

Returns:

  • (String)


175
176
177
178
179
180
181
182
183
# File 'lib/formatter/common.rb', line 175

def highlight(mark = PASS)
  case mark
  when PASS then rainbowify PASS_ARY[@color_index % 2]
  when FAIL then "\e[31m#{mark}\e[0m"
  when ERROR then "\e[33m#{mark}\e[0m"
  when PENDING then "\e[33m#{mark}\e[0m"
  else mark
  end
end

#padding_widthFixnum

Gets the padding for the current example count

Returns:

  • (Fixnum)


100
101
102
# File 'lib/formatter/common.rb', line 100

def padding_width
  @example_count.to_s.length * 2 + 6
end

#pending_color(text) ⇒ Object



223
224
225
# File 'lib/formatter/common.rb', line 223

def pending_color(text)
  wrap(text, :yellow)
end

#progress_linesArray

Determines line by line tail plus score output

Returns:

  • (Array)


61
62
63
64
65
66
67
68
69
# File 'lib/formatter/common.rb', line 61

def progress_lines
  rainbow_trails = rainbow_trail.split("\n")
  [
    rainbow_trails.each_with_index.inject([]) do |result, (trail, index)|
      value = trail_progress_line_score(index)
      result << "#{value} #{trail}"
    end
  ].flatten
end

#rainbow_trailString

Creates a rainbow trail

Returns:

  • (String)

    the sprintf format of the Nyan cat



134
135
136
137
138
139
140
# File 'lib/formatter/common.rb', line 134

def rainbow_trail
  marks = @example_results.each_with_index.map { |mark, i| highlight(mark) * example_width(i) }
  marks.shift(current_width - terminal_width) if current_width >= terminal_width
  ascii_to_display.split("\n").each_with_index.map do |line, _index|
    "#{marks.join}#{line}"
  end.join("\n")
end

#rainbowify(string) ⇒ String

Colorizes the string with raindow colors of the rainbow

Returns:

  • (String)


151
152
153
154
155
# File 'lib/formatter/common.rb', line 151

def rainbowify(string)
  c = colors[@color_index % colors.size]
  @color_index += 1
  "#{ESC}38;5;#{c}m#{string}#{NND}"
end

#scoreboardArray

Creates a data store of pass, failed, and pending example results We have to pad the results here because sprintf can’t properly pad color

Returns:

  • (Array)


121
122
123
124
125
126
127
128
129
# File 'lib/formatter/common.rb', line 121

def scoreboard
  @pending_examples ||= []
  @failed_examples ||= []
  padding = @example_count.to_s.length
  [@current.to_s.rjust(padding),
   success_color((@current - @pending_examples.size - @failed_examples.size).to_s.rjust(padding)),
   pending_color(@pending_examples.size.to_s.rjust(padding)),
   failure_color(@failed_examples.size.to_s.rjust(padding))]
end

#success_color(text) ⇒ Object



219
220
221
# File 'lib/formatter/common.rb', line 219

def success_color(text)
  wrap(text, :green)
end

#terminal_widthFixnum

A Unix trick using stty to get the console columns

Returns:

  • (Fixnum)


107
108
109
110
111
112
113
114
115
# File 'lib/formatter/common.rb', line 107

def terminal_width
  stty_size = `stty size 2>/dev/null`
  default_width = if !stty_size || defined? JRUBY_VERSION
                    80
                  else
                    stty_size.split.map(&:to_i).reverse.first - 1
                  end
  @terminal_width ||= default_width
end

#tick(mark: PASS) ⇒ Object

Increments the example count and displays the current progress



37
38
39
40
41
42
# File 'lib/formatter/common.rb', line 37

def tick(mark: PASS)
  @example_results << mark
  @current = @current > @example_count ? @example_count : @current + 1
  @animation_index = (@animation_index + 1) % ascii_array.size
  dump_progress
end

#trail_progress_line_score(trail_index) ⇒ Object



71
72
73
74
75
76
77
# File 'lib/formatter/common.rb', line 71

def trail_progress_line_score(trail_index)
  if scoreboard[trail_index]
    "#{scoreboard[trail_index]}/#{@example_count}:"
  else
    ' ' * "#{scoreboard[0]}/#{@example_count}:".size
  end
end

#wrap(text, code_or_symbol) ⇒ Object



241
242
243
244
245
246
247
# File 'lib/formatter/common.rb', line 241

def wrap(text, code_or_symbol)
  if RSpec.configuration.color_enabled?
    "\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m"
  else
    text
  end
end