cli-ui 1.2.0 → 1.4.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 +5 -5
- data/.dependabot/config.yml +8 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/probots.yml +2 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +23 -2
- data/.travis.yml +4 -2
- data/Gemfile.lock +56 -0
- data/README.md +50 -3
- data/Rakefile +1 -1
- data/cli-ui.gemspec +4 -4
- data/dev.yml +1 -1
- data/lib/cli/ui.rb +61 -20
- data/lib/cli/ui/ansi.rb +9 -3
- data/lib/cli/ui/color.rb +12 -7
- data/lib/cli/ui/formatter.rb +34 -21
- data/lib/cli/ui/frame.rb +108 -149
- data/lib/cli/ui/frame/frame_stack.rb +98 -0
- data/lib/cli/ui/frame/frame_style.rb +120 -0
- data/lib/cli/ui/frame/frame_style/box.rb +166 -0
- data/lib/cli/ui/frame/frame_style/bracket.rb +139 -0
- data/lib/cli/ui/glyph.rb +22 -17
- data/lib/cli/ui/os.rb +63 -0
- data/lib/cli/ui/printer.rb +47 -0
- data/lib/cli/ui/progress.rb +10 -8
- data/lib/cli/ui/prompt.rb +88 -17
- data/lib/cli/ui/prompt/interactive_options.rb +261 -50
- data/lib/cli/ui/prompt/options_handler.rb +7 -2
- data/lib/cli/ui/spinner.rb +23 -5
- data/lib/cli/ui/spinner/spin_group.rb +39 -11
- data/lib/cli/ui/stdout_router.rb +12 -7
- data/lib/cli/ui/terminal.rb +26 -16
- data/lib/cli/ui/truncater.rb +3 -3
- data/lib/cli/ui/version.rb +1 -1
- data/lib/cli/ui/widgets.rb +77 -0
- data/lib/cli/ui/widgets/base.rb +27 -0
- data/lib/cli/ui/widgets/status.rb +61 -0
- metadata +21 -24
- data/lib/cli/ui/box.rb +0 -15
data/lib/cli/ui/color.rb
CHANGED
@@ -31,19 +31,24 @@ module CLI
|
|
31
31
|
BOLD = new('1', :bold)
|
32
32
|
WHITE = new('97', :white)
|
33
33
|
|
34
|
+
# 240 is very dark gray; 255 is very light gray. 244 is somewhat dark.
|
35
|
+
GRAY = new('38;5;244', :grey)
|
36
|
+
|
34
37
|
MAP = {
|
35
|
-
red:
|
36
|
-
green:
|
37
|
-
yellow:
|
38
|
-
blue:
|
38
|
+
red: RED,
|
39
|
+
green: GREEN,
|
40
|
+
yellow: YELLOW,
|
41
|
+
blue: BLUE,
|
39
42
|
magenta: MAGENTA,
|
40
|
-
cyan:
|
41
|
-
reset:
|
42
|
-
bold:
|
43
|
+
cyan: CYAN,
|
44
|
+
reset: RESET,
|
45
|
+
bold: BOLD,
|
46
|
+
gray: GRAY,
|
43
47
|
}.freeze
|
44
48
|
|
45
49
|
class InvalidColorName < ArgumentError
|
46
50
|
def initialize(name)
|
51
|
+
super
|
47
52
|
@name = name
|
48
53
|
end
|
49
54
|
|
data/lib/cli/ui/formatter.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
4
|
-
require 'strscan'
|
2
|
+
require('cli/ui')
|
3
|
+
require('strscan')
|
5
4
|
|
6
5
|
module CLI
|
7
6
|
module UI
|
@@ -13,39 +12,40 @@ module CLI
|
|
13
12
|
#
|
14
13
|
SGR_MAP = {
|
15
14
|
# presentational
|
16
|
-
'red'
|
17
|
-
'green'
|
18
|
-
'yellow'
|
19
|
-
|
20
|
-
'blue'
|
21
|
-
'magenta'
|
22
|
-
'cyan'
|
23
|
-
'bold'
|
24
|
-
'italic'
|
15
|
+
'red' => '31',
|
16
|
+
'green' => '32',
|
17
|
+
'yellow' => '33',
|
18
|
+
# default blue is low-contrast against black in some default terminal color scheme
|
19
|
+
'blue' => '94', # 9x = high-intensity fg color x
|
20
|
+
'magenta' => '35',
|
21
|
+
'cyan' => '36',
|
22
|
+
'bold' => '1',
|
23
|
+
'italic' => '3',
|
25
24
|
'underline' => '4',
|
26
|
-
'reset'
|
25
|
+
'reset' => '0',
|
27
26
|
|
28
27
|
# semantic
|
29
|
-
'error'
|
28
|
+
'error' => '31', # red
|
30
29
|
'success' => '32', # success
|
31
30
|
'warning' => '33', # yellow
|
32
|
-
'info'
|
31
|
+
'info' => '94', # bright blue
|
33
32
|
'command' => '36', # cyan
|
34
33
|
}.freeze
|
35
34
|
|
36
35
|
BEGIN_EXPR = '{{'
|
37
36
|
END_EXPR = '}}'
|
38
37
|
|
38
|
+
SCAN_WIDGET = %r[@widget/(?<handle>\w+):(?<args>.*?)}}]
|
39
39
|
SCAN_FUNCNAME = /\w+:/
|
40
40
|
SCAN_GLYPH = /.}}/
|
41
|
-
SCAN_BODY =
|
41
|
+
SCAN_BODY = %r{
|
42
42
|
.*?
|
43
43
|
(
|
44
44
|
#{BEGIN_EXPR} |
|
45
45
|
#{END_EXPR} |
|
46
46
|
\z
|
47
47
|
)
|
48
|
-
|
48
|
+
}mx
|
49
49
|
|
50
50
|
DISCARD_BRACES = 0..-3
|
51
51
|
|
@@ -123,7 +123,7 @@ module CLI
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def parse_expr(sc, stack)
|
126
|
-
if match = sc.scan(SCAN_GLYPH)
|
126
|
+
if (match = sc.scan(SCAN_GLYPH))
|
127
127
|
glyph_handle = match[0]
|
128
128
|
begin
|
129
129
|
glyph = Glyph.lookup(glyph_handle)
|
@@ -136,7 +136,20 @@ module CLI
|
|
136
136
|
index
|
137
137
|
)
|
138
138
|
end
|
139
|
-
elsif match = sc.scan(
|
139
|
+
elsif (match = sc.scan(SCAN_WIDGET))
|
140
|
+
match_data = SCAN_WIDGET.match(match) # Regexp.last_match doesn't work here
|
141
|
+
widget_handle = match_data['handle']
|
142
|
+
begin
|
143
|
+
widget = Widgets.lookup(widget_handle)
|
144
|
+
emit(widget.call(match_data['args']), stack)
|
145
|
+
rescue Widgets::InvalidWidgetHandle
|
146
|
+
index = sc.pos - 2 # rewind past '}}'
|
147
|
+
raise(FormatError.new(
|
148
|
+
"invalid widget handle at index #{index}: '#{widget_handle}'",
|
149
|
+
@text, index,
|
150
|
+
))
|
151
|
+
end
|
152
|
+
elsif (match = sc.scan(SCAN_FUNCNAME))
|
140
153
|
funcname = match.chop
|
141
154
|
stack.push(funcname)
|
142
155
|
else
|
@@ -153,10 +166,10 @@ module CLI
|
|
153
166
|
|
154
167
|
def parse_body(sc, stack = [])
|
155
168
|
match = sc.scan(SCAN_BODY)
|
156
|
-
if match
|
169
|
+
if match&.end_with?(BEGIN_EXPR)
|
157
170
|
emit(match[DISCARD_BRACES], stack)
|
158
171
|
parse_expr(sc, stack)
|
159
|
-
elsif match
|
172
|
+
elsif match&.end_with?(END_EXPR)
|
160
173
|
emit(match[DISCARD_BRACES], stack)
|
161
174
|
if stack.pop == LITERAL_BRACES
|
162
175
|
emit('}}', stack)
|
data/lib/cli/ui/frame.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'cli/ui'
|
3
|
+
require 'cli/ui/frame/frame_stack'
|
4
|
+
require 'cli/ui/frame/frame_style'
|
2
5
|
|
3
6
|
module CLI
|
4
7
|
module UI
|
@@ -7,6 +10,22 @@ module CLI
|
|
7
10
|
class << self
|
8
11
|
DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
|
9
12
|
|
13
|
+
def frame_style
|
14
|
+
@frame_style ||= FrameStyle::Box
|
15
|
+
end
|
16
|
+
|
17
|
+
# Set the default frame style.
|
18
|
+
#
|
19
|
+
# Raises ArgumentError if +frame_style+ is not valid
|
20
|
+
#
|
21
|
+
# ==== Attributes
|
22
|
+
#
|
23
|
+
# * +symbol+ or +FrameStyle+ - the default frame style to use for frames
|
24
|
+
#
|
25
|
+
def frame_style=(frame_style)
|
26
|
+
@frame_style = CLI::UI.resolve_style(frame_style)
|
27
|
+
end
|
28
|
+
|
10
29
|
# Opens a new frame. Can be nested
|
11
30
|
# Can be invoked in two ways: block and blockless
|
12
31
|
# * In block form, the frame is closed automatically when the block returns
|
@@ -27,6 +46,7 @@ module CLI
|
|
27
46
|
# * +:failure_text+ - If the block failed, what do we output? Defaults to nil
|
28
47
|
# * +:success_text+ - If the block succeeds, what do we output? Defaults to nil
|
29
48
|
# * +:timing+ - How long did the frame content take? Invalid for blockless. Defaults to true for the block form
|
49
|
+
# * +frame_style+ - The frame style to use for this frame
|
30
50
|
#
|
31
51
|
# ==== Example
|
32
52
|
#
|
@@ -34,7 +54,7 @@ module CLI
|
|
34
54
|
#
|
35
55
|
# CLI::UI::Frame.open('Open') { puts 'hi' }
|
36
56
|
#
|
37
|
-
# Output:
|
57
|
+
# Default Output:
|
38
58
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
39
59
|
# ┃ hi
|
40
60
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (0.0s) ━━
|
@@ -43,7 +63,7 @@ module CLI
|
|
43
63
|
#
|
44
64
|
# CLI::UI::Frame.open('Open')
|
45
65
|
#
|
46
|
-
# Output:
|
66
|
+
# Default Output:
|
47
67
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
48
68
|
#
|
49
69
|
#
|
@@ -52,8 +72,10 @@ module CLI
|
|
52
72
|
color: DEFAULT_FRAME_COLOR,
|
53
73
|
failure_text: nil,
|
54
74
|
success_text: nil,
|
55
|
-
timing: nil
|
75
|
+
timing: nil,
|
76
|
+
frame_style: self.frame_style
|
56
77
|
)
|
78
|
+
frame_style = CLI::UI.resolve_style(frame_style)
|
57
79
|
color = CLI::UI.resolve_color(color)
|
58
80
|
|
59
81
|
unless block_given?
|
@@ -61,18 +83,17 @@ module CLI
|
|
61
83
|
raise ArgumentError, "failure_text is not compatible with blockless invocation"
|
62
84
|
elsif success_text
|
63
85
|
raise ArgumentError, "success_text is not compatible with blockless invocation"
|
64
|
-
elsif
|
86
|
+
elsif timing
|
65
87
|
raise ArgumentError, "timing is not compatible with blockless invocation"
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
69
|
-
|
70
|
-
|
71
|
-
t_start = Time.now.to_f
|
91
|
+
t_start = Time.now
|
72
92
|
CLI::UI.raw do
|
73
|
-
|
93
|
+
print(prefix.chop)
|
94
|
+
puts frame_style.open(text, color: color)
|
74
95
|
end
|
75
|
-
FrameStack.push(color)
|
96
|
+
FrameStack.push(color: color, style: frame_style)
|
76
97
|
|
77
98
|
return unless block_given?
|
78
99
|
|
@@ -82,14 +103,14 @@ module CLI
|
|
82
103
|
success = yield
|
83
104
|
rescue
|
84
105
|
closed = true
|
85
|
-
t_diff =
|
106
|
+
t_diff = elasped(t_start, timing)
|
86
107
|
close(failure_text, color: :red, elapsed: t_diff)
|
87
108
|
raise
|
88
109
|
else
|
89
110
|
success
|
90
111
|
ensure
|
91
112
|
unless closed
|
92
|
-
t_diff =
|
113
|
+
t_diff = elasped(t_start, timing)
|
93
114
|
if success != false
|
94
115
|
close(success_text, color: color, elapsed: t_diff)
|
95
116
|
else
|
@@ -99,8 +120,8 @@ module CLI
|
|
99
120
|
end
|
100
121
|
end
|
101
122
|
|
102
|
-
#
|
103
|
-
#
|
123
|
+
# Adds a divider in a frame
|
124
|
+
# Used to separate information within a single frame
|
104
125
|
#
|
105
126
|
# ==== Attributes
|
106
127
|
#
|
@@ -109,31 +130,38 @@ module CLI
|
|
109
130
|
# ==== Options
|
110
131
|
#
|
111
132
|
# * +:color+ - The color of the frame. Defaults to +DEFAULT_FRAME_COLOR+
|
112
|
-
# *
|
133
|
+
# * +frame_style+ - The frame style to use for this frame
|
113
134
|
#
|
114
135
|
# ==== Example
|
115
136
|
#
|
116
|
-
# CLI::UI::Frame.
|
137
|
+
# CLI::UI::Frame.open('Open') { CLI::UI::Frame.divider('Divider') }
|
117
138
|
#
|
118
|
-
# Output:
|
119
|
-
#
|
139
|
+
# Default Output:
|
140
|
+
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
141
|
+
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
142
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
120
143
|
#
|
144
|
+
# ==== Raises
|
121
145
|
#
|
122
|
-
|
123
|
-
|
146
|
+
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
147
|
+
#
|
148
|
+
def divider(text, color: nil, frame_style: nil)
|
149
|
+
fs_item = FrameStack.pop
|
150
|
+
raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item
|
151
|
+
|
152
|
+
color = CLI::UI.resolve_color(color) || fs_item.color
|
153
|
+
frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
|
124
154
|
|
125
|
-
FrameStack.pop
|
126
|
-
kwargs = {}
|
127
|
-
if elapsed
|
128
|
-
kwargs[:right_text] = "(#{elapsed.round(2)}s)"
|
129
|
-
end
|
130
155
|
CLI::UI.raw do
|
131
|
-
|
156
|
+
print(prefix.chop)
|
157
|
+
puts frame_style.divider(text, color: color)
|
132
158
|
end
|
159
|
+
|
160
|
+
FrameStack.push(fs_item)
|
133
161
|
end
|
134
162
|
|
135
|
-
#
|
136
|
-
#
|
163
|
+
# Closes a frame
|
164
|
+
# Automatically called for a block-form +open+
|
137
165
|
#
|
138
166
|
# ==== Attributes
|
139
167
|
#
|
@@ -141,51 +169,70 @@ module CLI
|
|
141
169
|
#
|
142
170
|
# ==== Options
|
143
171
|
#
|
144
|
-
# * +:color+ - The color of the frame. Defaults to
|
172
|
+
# * +:color+ - The color of the frame. Defaults to nil
|
173
|
+
# * +:elapsed+ - How long did the frame take? Defaults to nil
|
174
|
+
# * +frame_style+ - The frame style to use for this frame. Defaults to nil
|
145
175
|
#
|
146
176
|
# ==== Example
|
147
177
|
#
|
148
|
-
# CLI::UI::Frame.
|
178
|
+
# CLI::UI::Frame.close('Close')
|
149
179
|
#
|
150
|
-
# Output:
|
151
|
-
#
|
152
|
-
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
153
|
-
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
180
|
+
# Default Output:
|
181
|
+
# ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
154
182
|
#
|
155
183
|
# ==== Raises
|
156
184
|
#
|
157
185
|
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
158
186
|
#
|
159
|
-
def
|
187
|
+
def close(text, color: nil, elapsed: nil, frame_style: nil)
|
160
188
|
fs_item = FrameStack.pop
|
161
|
-
raise UnnestedFrameException, "
|
162
|
-
|
163
|
-
|
189
|
+
raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item
|
190
|
+
|
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
|
164
198
|
|
165
199
|
CLI::UI.raw do
|
166
|
-
|
200
|
+
print(prefix.chop)
|
201
|
+
puts frame_style.close(text, color: color, **kwargs)
|
167
202
|
end
|
168
|
-
FrameStack.push(item)
|
169
203
|
end
|
170
204
|
|
171
205
|
# Determines the prefix of a frame entry taking multi-nested frames into account
|
172
206
|
#
|
173
207
|
# ==== Options
|
174
208
|
#
|
175
|
-
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
209
|
+
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
176
210
|
#
|
177
|
-
def prefix(color:
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
211
|
+
def prefix(color: Thread.current[:cliui_frame_color_override])
|
212
|
+
+''.tap do |output|
|
213
|
+
items = FrameStack.items
|
214
|
+
|
215
|
+
items[0..-2].each do |item|
|
216
|
+
output << item.color.code << item.frame_style.prefix
|
217
|
+
end
|
218
|
+
|
219
|
+
if (item = items.last)
|
220
|
+
final_color = color || item.color
|
221
|
+
output << CLI::UI.resolve_color(final_color).code \
|
222
|
+
<< item.frame_style.prefix \
|
223
|
+
<< ' ' \
|
224
|
+
<< CLI::UI::Color::RESET.code
|
225
|
+
end
|
182
226
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
227
|
+
end
|
228
|
+
|
229
|
+
# The width of a prefix given the number of Frames in the stack
|
230
|
+
def prefix_width
|
231
|
+
w = FrameStack.items.reduce(0) do |width, item|
|
232
|
+
width + item.frame_style.prefix_width
|
187
233
|
end
|
188
|
-
|
234
|
+
|
235
|
+
w.zero? ? w : w + 1
|
189
236
|
end
|
190
237
|
|
191
238
|
# Override a color for a given thread.
|
@@ -202,107 +249,19 @@ module CLI
|
|
202
249
|
Thread.current[:cliui_frame_color_override] = prev
|
203
250
|
end
|
204
251
|
|
205
|
-
# The width of a prefix given the number of Frames in the stack
|
206
|
-
#
|
207
|
-
def prefix_width
|
208
|
-
w = FrameStack.items.size
|
209
|
-
w.zero? ? 0 : w + 1
|
210
|
-
end
|
211
|
-
|
212
252
|
private
|
213
253
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
prefix << ' ' << text << ' '
|
226
|
-
end
|
227
|
-
|
228
|
-
termwidth = CLI::UI::Terminal.width
|
229
|
-
|
230
|
-
suffix = +''
|
231
|
-
if right_text
|
232
|
-
suffix << ' ' << right_text << ' '
|
233
|
-
end
|
234
|
-
|
235
|
-
suffix_width = CLI::UI::ANSI.printing_width(suffix)
|
236
|
-
suffix_end = termwidth - 2
|
237
|
-
suffix_start = suffix_end - suffix_width
|
238
|
-
|
239
|
-
prefix_width = CLI::UI::ANSI.printing_width(prefix)
|
240
|
-
prefix_start = 0
|
241
|
-
prefix_end = prefix_start + prefix_width
|
242
|
-
|
243
|
-
if prefix_end > suffix_start
|
244
|
-
suffix = ''
|
245
|
-
# if prefix_end > termwidth
|
246
|
-
# we *could* truncate it, but let's just let it overflow to the
|
247
|
-
# next line and call it poor usage of this API.
|
248
|
-
end
|
249
|
-
|
250
|
-
o = +''
|
251
|
-
|
252
|
-
is_ci = ![0, '', nil].include?(ENV['CI'])
|
253
|
-
|
254
|
-
# Jumping around the line can cause some unwanted flashes
|
255
|
-
o << CLI::UI::ANSI.hide_cursor
|
256
|
-
|
257
|
-
o << if is_ci
|
258
|
-
# In CI, we can't use absolute horizontal positions because of timestamps.
|
259
|
-
# So we move around the line by offset from this cursor position.
|
260
|
-
CLI::UI::ANSI.cursor_save
|
261
|
-
else
|
262
|
-
# Outside of CI, we reset to column 1 so that things like ^C don't
|
263
|
-
# cause output misformatting.
|
264
|
-
"\r"
|
265
|
-
end
|
266
|
-
|
267
|
-
o << color.code
|
268
|
-
o << CLI::UI::Box::Heavy::HORZ * termwidth # draw a full line
|
269
|
-
o << print_at_x(prefix_start, prefix, is_ci)
|
270
|
-
o << color.code
|
271
|
-
o << print_at_x(suffix_start, suffix, is_ci)
|
272
|
-
o << CLI::UI::Color::RESET.code
|
273
|
-
o << CLI::UI::ANSI.show_cursor
|
274
|
-
o << "\n"
|
275
|
-
|
276
|
-
o
|
277
|
-
end
|
278
|
-
|
279
|
-
def print_at_x(x, str, is_ci)
|
280
|
-
if is_ci
|
281
|
-
CLI::UI::ANSI.cursor_restore + CLI::UI::ANSI.cursor_forward(x) + str
|
282
|
-
else
|
283
|
-
CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
module FrameStack
|
288
|
-
ENVVAR = 'CLI_FRAME_STACK'
|
289
|
-
|
290
|
-
def self.items
|
291
|
-
ENV.fetch(ENVVAR, '').split(':').map(&:to_sym)
|
292
|
-
end
|
293
|
-
|
294
|
-
def self.push(item)
|
295
|
-
curr = items
|
296
|
-
curr << item.name
|
297
|
-
ENV[ENVVAR] = curr.join(':')
|
298
|
-
end
|
299
|
-
|
300
|
-
def self.pop
|
301
|
-
curr = items
|
302
|
-
ret = curr.pop
|
303
|
-
ENV[ENVVAR] = curr.join(':')
|
304
|
-
ret.nil? ? nil : ret.to_sym
|
305
|
-
end
|
254
|
+
# If timing is:
|
255
|
+
# Numeric: return it
|
256
|
+
# false: return nil
|
257
|
+
# true or nil: defaults to Time.new
|
258
|
+
# Time: return the difference with start
|
259
|
+
def elasped(start, timing)
|
260
|
+
return timing if timing.is_a?(Numeric)
|
261
|
+
return if timing.is_a?(FalseClass)
|
262
|
+
|
263
|
+
timing = Time.new if timing.is_a?(TrueClass) || timing.nil?
|
264
|
+
timing - start
|
306
265
|
end
|
307
266
|
end
|
308
267
|
end
|