cli-ui 1.5.0 → 2.0.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.
@@ -1,3 +1,5 @@
1
+ # typed: true
2
+
1
3
  module CLI
2
4
  module UI
3
5
  module Frame
@@ -6,8 +8,18 @@ module CLI
6
8
  STYLE_ENVVAR = 'CLI_STYLE_STACK'
7
9
 
8
10
  class StackItem
9
- attr_reader :color, :frame_style
11
+ extend T::Sig
12
+
13
+ sig { returns(CLI::UI::Color) }
14
+ attr_reader :color
15
+
16
+ sig { returns(CLI::UI::Frame::FrameStyle) }
17
+ attr_reader :frame_style
10
18
 
19
+ sig do
20
+ params(color_name: CLI::UI::Colorable, style_name: FrameStylable)
21
+ .void
22
+ end
11
23
  def initialize(color_name, style_name)
12
24
  @color = CLI::UI.resolve_color(color_name)
13
25
  @frame_style = CLI::UI.resolve_style(style_name)
@@ -15,13 +27,16 @@ module CLI
15
27
  end
16
28
 
17
29
  class << self
30
+ extend T::Sig
31
+
18
32
  # Fetch all items off the frame stack
33
+ sig { returns(T::Array[StackItem]) }
19
34
  def items
20
35
  colors = ENV.fetch(COLOR_ENVVAR, '').split(':').map(&:to_sym)
21
36
  styles = ENV.fetch(STYLE_ENVVAR, '').split(':').map(&:to_sym)
22
37
 
23
- colors.length.times.map do |i|
24
- StackItem.new(colors[i], styles[i] || Frame.frame_style)
38
+ colors.each_with_index.map do |color, i|
39
+ StackItem.new(color, styles[i] || Frame.frame_style)
25
40
  end
26
41
  end
27
42
 
@@ -43,18 +58,20 @@ module CLI
43
58
  # If both an item and a color/style pair are given, raises an +ArgumentError+
44
59
  # If the given item is not a +StackItem+, raises an +ArgumentError+
45
60
  #
61
+ sig do
62
+ params(
63
+ item: T.nilable(StackItem),
64
+ color: T.nilable(CLI::UI::Color),
65
+ style: T.nilable(CLI::UI::Frame::FrameStyle),
66
+ )
67
+ .void
68
+ end
46
69
  def push(item = nil, color: nil, style: nil)
47
- unless item.nil?
48
- unless item.is_a?(StackItem)
49
- raise ArgumentError, 'item must be a StackItem'
50
- end
51
-
52
- unless color.nil? && style.nil?
53
- raise ArgumentError, 'Must give one of item or color: and style:'
54
- end
70
+ if color.nil? != style.nil? || item.nil? == color.nil?
71
+ raise ArgumentError, 'Must give one of item or color: and style:'
55
72
  end
56
73
 
57
- item ||= StackItem.new(color, style)
74
+ item ||= StackItem.new(T.must(color), T.must(style))
58
75
 
59
76
  curr = items
60
77
  curr << item
@@ -63,6 +80,7 @@ module CLI
63
80
  end
64
81
 
65
82
  # Removes and returns the last stack item off the stack
83
+ sig { returns(T.nilable(StackItem)) }
66
84
  def pop
67
85
  curr = items
68
86
  ret = curr.pop
@@ -79,13 +97,14 @@ module CLI
79
97
  # This is done to preserve backward compatibility with earlier versions of cli/ui.
80
98
  # This ensures that any code that relied upon previous stack behavior should continue
81
99
  # to work.
100
+ sig { params(items: T::Array[StackItem]).void }
82
101
  def serialize(items)
83
102
  colors = []
84
103
  styles = []
85
104
 
86
105
  items.each do |item|
87
106
  colors << item.color.name
88
- styles << item.frame_style.name
107
+ styles << item.frame_style.style_name
89
108
  end
90
109
 
91
110
  ENV[COLOR_ENVVAR] = colors.join(':')
@@ -1,3 +1,5 @@
1
+ # typed: true
2
+
1
3
  module CLI
2
4
  module UI
3
5
  module Frame
@@ -12,10 +14,14 @@ module CLI
12
14
  BOTTOM_LEFT = '┗'
13
15
 
14
16
  class << self
15
- def name
16
- 'box'
17
+ extend T::Sig
18
+
19
+ sig { override.returns(Symbol) }
20
+ def style_name
21
+ :box
17
22
  end
18
23
 
24
+ sig { override.returns(String) }
19
25
  def prefix
20
26
  VERTICAL
21
27
  end
@@ -34,7 +40,8 @@ module CLI
34
40
  #
35
41
  # ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
42
  #
37
- def open(text, color:)
43
+ sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
44
+ def start(text, color:)
38
45
  edge(text, color: color, first: TOP_LEFT)
39
46
  end
40
47
 
@@ -52,6 +59,7 @@ module CLI
52
59
  #
53
60
  # ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
54
61
  #
62
+ sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
55
63
  def divider(text, color:)
56
64
  edge(text, color: color, first: DIVIDER)
57
65
  end
@@ -71,12 +79,16 @@ module CLI
71
79
  #
72
80
  # ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
73
81
  #
82
+ sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
74
83
  def close(text, color:, right_text: nil)
75
84
  edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
76
85
  end
77
86
 
78
87
  private
79
88
 
89
+ sig do
90
+ params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
91
+ end
80
92
  def edge(text, color:, first:, right_text: nil)
81
93
  color = CLI::UI.resolve_color(color)
82
94
 
@@ -84,7 +96,6 @@ module CLI
84
96
 
85
97
  preamble << color.code << first << (HORIZONTAL * 2)
86
98
 
87
- text ||= ''
88
99
  unless text.empty?
89
100
  preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
90
101
  end
@@ -1,3 +1,5 @@
1
+ # typed: true
2
+
1
3
  module CLI
2
4
  module UI
3
5
  module Frame
@@ -12,10 +14,14 @@ module CLI
12
14
  BOTTOM_LEFT = '┗'
13
15
 
14
16
  class << self
15
- def name
16
- 'bracket'
17
+ extend T::Sig
18
+
19
+ sig { override.returns(Symbol) }
20
+ def style_name
21
+ :bracket
17
22
  end
18
23
 
24
+ sig { override.returns(String) }
19
25
  def prefix
20
26
  VERTICAL
21
27
  end
@@ -34,7 +40,8 @@ module CLI
34
40
  #
35
41
  # ┏━━ Open
36
42
  #
37
- def open(text, color:)
43
+ sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
44
+ def start(text, color:)
38
45
  edge(text, color: color, first: TOP_LEFT)
39
46
  end
40
47
 
@@ -52,6 +59,7 @@ module CLI
52
59
  #
53
60
  # ┣━━ Divider
54
61
  #
62
+ sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
55
63
  def divider(text, color:)
56
64
  edge(text, color: color, first: DIVIDER)
57
65
  end
@@ -71,12 +79,16 @@ module CLI
71
79
  #
72
80
  # ┗━━ Close
73
81
  #
82
+ sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
74
83
  def close(text, color:, right_text: nil)
75
84
  edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
76
85
  end
77
86
 
78
87
  private
79
88
 
89
+ sig do
90
+ params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
91
+ end
80
92
  def edge(text, color:, first:, right_text: nil)
81
93
  color = CLI::UI.resolve_color(color)
82
94
 
@@ -84,7 +96,6 @@ module CLI
84
96
 
85
97
  preamble << color.code << first << (HORIZONTAL * 2)
86
98
 
87
- text ||= ''
88
99
  unless text.empty?
89
100
  preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
90
101
  end
@@ -117,16 +128,16 @@ module CLI
117
128
  # the final space, since we're going to write over it.
118
129
  preamble_start -= 1 unless preamble_start.zero?
119
130
 
120
- # Prefix_width includes the width of the terminal space, which we
121
- # want to remove. The clamping is done to avoid a negative
122
- # preamble start which can occur for the first frame.
131
+ # Jumping around the line can cause some unwanted flashes
123
132
  o << CLI::UI::ANSI.hide_cursor
124
133
 
125
134
  # reset to column 1 so that things like ^C don't ruin formatting
126
135
  o << "\r"
136
+
127
137
  o << color.code
128
138
  o << print_at_x(preamble_start, preamble + color.code + suffix)
129
139
  o << CLI::UI::Color::RESET.code
140
+ o << CLI::UI::ANSI.show_cursor
130
141
  o << "\n"
131
142
 
132
143
  o
@@ -1,120 +1,117 @@
1
+ # typed: true
2
+
1
3
  require 'cli/ui/frame'
2
4
 
3
5
  module CLI
4
6
  module UI
5
7
  module Frame
6
8
  module FrameStyle
7
- class << self
8
- # rubocop:disable Style/ClassVars
9
- @@loaded_styles = []
9
+ include Kernel
10
+ extend T::Sig
11
+ extend T::Helpers
12
+ abstract!
10
13
 
11
- def loaded_styles
12
- @@loaded_styles.map(&:name)
13
- end
14
+ autoload(:Box, 'cli/ui/frame/frame_style/box')
15
+ autoload(:Bracket, 'cli/ui/frame/frame_style/bracket')
16
+
17
+ MAP = {
18
+ box: -> { FrameStyle::Box },
19
+ bracket: -> { FrameStyle::Bracket },
20
+ }
21
+
22
+ class << self
23
+ extend T::Sig
14
24
 
15
25
  # Lookup a frame style via its name
16
26
  #
17
27
  # ==== Attributes
18
28
  #
19
29
  # * +symbol+ - frame style name to lookup
30
+ sig { params(name: T.any(String, Symbol)).returns(FrameStyle) }
20
31
  def lookup(name)
21
- @@loaded_styles
22
- .find { |style| style.name.to_sym == name }
23
- .tap { |style| raise InvalidFrameStyleName, name if style.nil? }
32
+ MAP.fetch(name.to_sym).call
33
+ rescue KeyError
34
+ raise(InvalidFrameStyleName, name)
24
35
  end
25
-
26
- def extended(base)
27
- @@loaded_styles << base
28
- base.extend(Interface)
29
- end
30
- # rubocop:enable Style/ClassVars
31
36
  end
32
37
 
33
- class InvalidFrameStyleName < ArgumentError
34
- def initialize(name)
35
- super
36
- @name = name
37
- end
38
+ sig { abstract.returns(Symbol) }
39
+ def style_name; end
38
40
 
39
- def message
40
- keys = FrameStyle.loaded_styles.map(&:inspect).join(',')
41
- "invalid frame style: #{@name.inspect}" \
42
- ' -- must be one of CLI::UI::Frame::FrameStyle.loaded_styles ' \
43
- "(#{keys})"
44
- end
41
+ # Returns the character(s) that should be printed at the beginning
42
+ # of lines inside this frame
43
+ sig { abstract.returns(String) }
44
+ def prefix; end
45
+
46
+ # Returns the printing width of the prefix
47
+ sig { returns(Integer) }
48
+ def prefix_width
49
+ CLI::UI::ANSI.printing_width(prefix)
45
50
  end
46
51
 
47
- # Public interface for FrameStyles
48
- # Applied by extending FrameStyle
49
- module Interface
50
- def name
51
- raise NotImplementedError
52
- end
52
+ # Draws the "Open" line for this frame style
53
+ #
54
+ # ==== Attributes
55
+ #
56
+ # * +text+ - (required) the text/title to output in the frame
57
+ #
58
+ # ==== Options
59
+ #
60
+ # * +:color+ - (required) The color of the frame.
61
+ #
62
+ sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
63
+ def start(text, color:); end
53
64
 
54
- # Returns the character(s) that should be printed at the beginning
55
- # of lines inside this frame
56
- def prefix
57
- raise NotImplementedError
58
- end
65
+ # Draws the "Close" line for this frame style
66
+ #
67
+ # ==== Attributes
68
+ #
69
+ # * +text+ - (required) the text/title to output in the frame
70
+ #
71
+ # ==== Options
72
+ #
73
+ # * +:color+ - (required) The color of the frame.
74
+ # * +:right_text+ - Text to print at the right of the line. Defaults to nil
75
+ #
76
+ sig { abstract.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
77
+ def close(text, color:, right_text: nil); end
59
78
 
60
- # Returns the printing width of the prefix
61
- def prefix_width
62
- CLI::UI::ANSI.printing_width(prefix)
63
- end
79
+ # Draws a "divider" line for the current frame style
80
+ #
81
+ # ==== Attributes
82
+ #
83
+ # * +text+ - (required) the text/title to output in the frame
84
+ #
85
+ # ==== Options
86
+ #
87
+ # * +:color+ - (required) The color of the frame.
88
+ #
89
+ sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
90
+ def divider(text, color:); end
64
91
 
65
- # Draws the "Open" line for this frame style
66
- #
67
- # ==== Attributes
68
- #
69
- # * +text+ - (required) the text/title to output in the frame
70
- #
71
- # ==== Options
72
- #
73
- # * +:color+ - (required) The color of the frame.
74
- #
75
- def open(text, color:)
76
- raise NotImplementedError
77
- end
92
+ sig { params(x: Integer, str: String).returns(String) }
93
+ def print_at_x(x, str)
94
+ CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
95
+ end
78
96
 
79
- # Draws the "Close" line for this frame style
80
- #
81
- # ==== Attributes
82
- #
83
- # * +text+ - (required) the text/title to output in the frame
84
- #
85
- # ==== Options
86
- #
87
- # * +:color+ - (required) The color of the frame.
88
- # * +:right_text+ - Text to print at the right of the line. Defaults to nil
89
- #
90
- def close(text, color:, right_text: nil)
91
- raise NotImplementedError
92
- end
97
+ class InvalidFrameStyleName < ArgumentError
98
+ extend T::Sig
93
99
 
94
- # Draws a "divider" line for the current frame style
95
- #
96
- # ==== Attributes
97
- #
98
- # * +text+ - (required) the text/title to output in the frame
99
- #
100
- # ==== Options
101
- #
102
- # * +:color+ - (required) The color of the frame.
103
- #
104
- def divider(text, color: nil)
105
- raise NotImplementedError
100
+ sig { params(name: T.any(String, Symbol)).void }
101
+ def initialize(name)
102
+ super
103
+ @name = name
106
104
  end
107
105
 
108
- private
109
-
110
- def print_at_x(x, str)
111
- CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
106
+ sig { returns(String) }
107
+ def message
108
+ keys = FrameStyle::MAP.keys.map(&:inspect).join(', ')
109
+ "invalid frame style: #{@name.inspect}" \
110
+ ' -- must be one of CLI::UI::Frame::FrameStyle::MAP ' \
111
+ "(#{keys})"
112
112
  end
113
113
  end
114
114
  end
115
115
  end
116
116
  end
117
117
  end
118
-
119
- require 'cli/ui/frame/frame_style/box'
120
- require 'cli/ui/frame/frame_style/bracket'
data/lib/cli/ui/frame.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # coding: utf-8
2
+
3
+ # typed: true
4
+
2
5
  require 'cli/ui'
3
6
  require 'cli/ui/frame/frame_stack'
4
7
  require 'cli/ui/frame/frame_style'
@@ -7,9 +10,12 @@ module CLI
7
10
  module UI
8
11
  module Frame
9
12
  class UnnestedFrameException < StandardError; end
13
+ DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
14
+
10
15
  class << self
11
- DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
16
+ extend T::Sig
12
17
 
18
+ sig { returns(FrameStyle) }
13
19
  def frame_style
14
20
  @frame_style ||= FrameStyle::Box
15
21
  end
@@ -22,6 +28,7 @@ module CLI
22
28
  #
23
29
  # * +symbol+ or +FrameStyle+ - the default frame style to use for frames
24
30
  #
31
+ sig { params(frame_style: FrameStylable).void }
25
32
  def frame_style=(frame_style)
26
33
  @frame_style = CLI::UI.resolve_style(frame_style)
27
34
  end
@@ -67,13 +74,25 @@ module CLI
67
74
  # ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
68
75
  #
69
76
  #
77
+ sig do
78
+ type_parameters(:T).params(
79
+ text: String,
80
+ color: Colorable,
81
+ failure_text: T.nilable(String),
82
+ success_text: T.nilable(String),
83
+ timing: T.any(T::Boolean, Numeric),
84
+ frame_style: FrameStylable,
85
+ block: T.nilable(T.proc.returns(T.type_parameter(:T))),
86
+ ).returns(T.nilable(T.type_parameter(:T)))
87
+ end
70
88
  def open(
71
89
  text,
72
90
  color: DEFAULT_FRAME_COLOR,
73
91
  failure_text: nil,
74
92
  success_text: nil,
75
- timing: nil,
76
- frame_style: self.frame_style
93
+ timing: block_given?,
94
+ frame_style: self.frame_style,
95
+ &block
77
96
  )
78
97
  frame_style = CLI::UI.resolve_style(frame_style)
79
98
  color = CLI::UI.resolve_color(color)
@@ -91,7 +110,7 @@ module CLI
91
110
  t_start = Time.now
92
111
  CLI::UI.raw do
93
112
  print(prefix.chop)
94
- puts frame_style.open(text, color: color)
113
+ puts frame_style.start(text, color: color)
95
114
  end
96
115
  FrameStack.push(color: color, style: frame_style)
97
116
 
@@ -103,15 +122,15 @@ module CLI
103
122
  success = yield
104
123
  rescue
105
124
  closed = true
106
- t_diff = elasped(t_start, timing)
125
+ t_diff = elapsed(t_start, timing)
107
126
  close(failure_text, color: :red, elapsed: t_diff)
108
127
  raise
109
128
  else
110
129
  success
111
130
  ensure
112
131
  unless closed
113
- t_diff = elasped(t_start, timing)
114
- if success != false
132
+ t_diff = elapsed(t_start, timing)
133
+ if T.unsafe(success) != false
115
134
  close(success_text, color: color, elapsed: t_diff)
116
135
  else
117
136
  close(failure_text, color: :red, elapsed: t_diff)
@@ -145,16 +164,17 @@ module CLI
145
164
  #
146
165
  # MUST be inside an open frame or it raises a +UnnestedFrameException+
147
166
  #
167
+ sig { params(text: T.nilable(String), color: T.nilable(Colorable), frame_style: T.nilable(FrameStylable)).void }
148
168
  def divider(text, color: nil, frame_style: nil)
149
169
  fs_item = FrameStack.pop
150
170
  raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
151
171
 
152
- color = CLI::UI.resolve_color(color) || fs_item.color
153
- frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
172
+ divider_color = CLI::UI.resolve_color(color || fs_item.color)
173
+ frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
154
174
 
155
175
  CLI::UI.raw do
156
176
  print(prefix.chop)
157
- puts frame_style.divider(text, color: color)
177
+ puts frame_style.divider(text.to_s, color: divider_color)
158
178
  end
159
179
 
160
180
  FrameStack.push(fs_item)
@@ -184,21 +204,25 @@ module CLI
184
204
  #
185
205
  # MUST be inside an open frame or it raises a +UnnestedFrameException+
186
206
  #
207
+ sig do
208
+ params(
209
+ text: T.nilable(String),
210
+ color: T.nilable(Colorable),
211
+ elapsed: T.nilable(Numeric),
212
+ frame_style: T.nilable(FrameStylable),
213
+ ).void
214
+ end
187
215
  def close(text, color: nil, elapsed: nil, frame_style: nil)
188
216
  fs_item = FrameStack.pop
189
217
  raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
190
218
 
191
- color = CLI::UI.resolve_color(color) || fs_item.color
192
- frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
193
-
194
- kwargs = {}
195
- if elapsed
196
- kwargs[:right_text] = "(#{elapsed.round(2)}s)"
197
- end
219
+ close_color = CLI::UI.resolve_color(color || fs_item.color)
220
+ frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
221
+ elapsed_string = elapsed ? "(#{elapsed.round(2)}s)" : nil
198
222
 
199
223
  CLI::UI.raw do
200
224
  print(prefix.chop)
201
- puts frame_style.close(text, color: color, **kwargs)
225
+ puts frame_style.close(text.to_s, color: close_color, right_text: elapsed_string)
202
226
  end
203
227
  end
204
228
 
@@ -208,11 +232,12 @@ module CLI
208
232
  #
209
233
  # * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
210
234
  #
235
+ sig { params(color: T.nilable(Colorable)).returns(String) }
211
236
  def prefix(color: Thread.current[:cliui_frame_color_override])
212
237
  +''.tap do |output|
213
238
  items = FrameStack.items
214
239
 
215
- items[0..-2].each do |item|
240
+ items[0..-2].to_a.each do |item|
216
241
  output << item.color.code << item.frame_style.prefix
217
242
  end
218
243
 
@@ -227,6 +252,7 @@ module CLI
227
252
  end
228
253
 
229
254
  # The width of a prefix given the number of Frames in the stack
255
+ sig { returns(Integer) }
230
256
  def prefix_width
231
257
  w = FrameStack.items.reduce(0) do |width, item|
232
258
  width + item.frame_style.prefix_width
@@ -241,7 +267,12 @@ module CLI
241
267
  #
242
268
  # * +color+ - The color to override to
243
269
  #
244
- def with_frame_color_override(color)
270
+ sig do
271
+ type_parameters(:T)
272
+ .params(color: Colorable, block: T.proc.returns(T.type_parameter(:T)))
273
+ .returns(T.type_parameter(:T))
274
+ end
275
+ def with_frame_color_override(color, &block)
245
276
  prev = Thread.current[:cliui_frame_color_override]
246
277
  Thread.current[:cliui_frame_color_override] = color
247
278
  yield
@@ -254,13 +285,13 @@ module CLI
254
285
  # If timing is:
255
286
  # Numeric: return it
256
287
  # false: return nil
257
- # true or nil: defaults to Time.new
258
- # Time: return the difference with start
259
- def elasped(start, timing)
288
+ # true: defaults to Time.new
289
+ sig { params(start: Time, timing: T.any(Numeric, T::Boolean)).returns(T.nilable(Numeric)) }
290
+ def elapsed(start, timing)
260
291
  return timing if timing.is_a?(Numeric)
261
292
  return if timing.is_a?(FalseClass)
262
293
 
263
- timing = Time.new if timing.is_a?(TrueClass) || timing.nil?
294
+ timing = Time.new
264
295
  timing - start
265
296
  end
266
297
  end